blob: 9f884b9995b8457c269fa2b7b204cfbe1f0ad2dc [file] [log] [blame]
David Tolnay4b4c4b62018-01-06 13:48:05 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
Nika Layzell27726662017-10-24 23:16:35 -04003//! discovers structs and enums declared with the `ast_*` macros and generates
4//! the functions for those types.
5//!
6//! It makes a few assumptions about the target crate:
7//! 1. All structs which are discovered must be re-exported in the root of the
8//! crate, even if they were declared in a submodule.
9//! 2. This code cannot discover submodules which are located in subdirectories
10//! - only submodules located in the same directory.
11//! 3. The path to `syn` is hardcoded.
12
David Tolnay6af48992018-08-01 11:16:28 -070013#![recursion_limit = "128"]
David Tolnay6b46a702018-08-01 23:51:32 -070014#![cfg_attr(
15 feature = "cargo-clippy",
16 allow(
17 needless_pass_by_value,
18 redundant_closure,
19 write_with_newline,
20 )
21)]
David Tolnayea9ae892017-12-26 01:44:32 -050022
David Tolnayd67fb752017-12-27 13:50:29 -050023#[macro_use]
24extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040025extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050026extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050027#[macro_use]
28extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050029#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050030extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070031extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040032
David Tolnayd67fb752017-12-27 13:50:29 -050033use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070034use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070035use quote::ToTokens;
36use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040037
David Tolnay01ed0ce2018-05-20 20:18:14 -070038use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050039use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040040use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070041use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040042use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040043
44const SYN_CRATE_ROOT: &str = "../src/lib.rs";
45
46const FOLD_SRC: &str = "../src/gen/fold.rs";
47const VISIT_SRC: &str = "../src/gen/visit.rs";
48const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
49
David Tolnayd67fb752017-12-27 13:50:29 -050050const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040051
Alex Crichton131308c2018-05-18 14:00:24 -070052const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050053
Alex Crichtond261d092018-05-18 13:47:35 -070054const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050055
Alex Crichtona74a1c82018-05-16 10:20:44 -070056fn path_eq(a: &syn::Path, b: &str) -> bool {
David Tolnay78b16142018-09-01 16:53:07 -070057 a.leading_colon.is_none()
58 && a.segments.len() == 1
59 && a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040060}
61
Alex Crichton715862b2018-05-17 12:31:49 -070062fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040063 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070064 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040065 attr.to_tokens(&mut features);
66 }
67 }
68 features
69}
70
71#[derive(Clone)]
72pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050073 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070074 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040075 // True if this is an ast_enum_of_structs! item with a #full annotation.
76 eos_full: bool,
77}
78
David Tolnayf0d63bf2017-12-26 12:29:47 -050079impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040080 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050082 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040083 .field("features", &self.features.to_string())
84 .finish()
85 }
86}
87
88// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050089type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040090
David Tolnay01ed0ce2018-05-20 20:18:14 -070091fn load_file<P: AsRef<Path>>(
92 name: P,
93 features: &TokenStream,
94 lookup: &mut Lookup,
95) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040096 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050097 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040098
99 let mut f = File::open(name)?;
100 let mut src = String::new();
101 f.read_to_string(&mut src)?;
102
103 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500104 let file =
105 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400106
107 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500108 'items: for item in file.items {
109 match item {
110 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400111 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800112 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400113 continue;
114 }
115
116 // We don't want to try to load the generated rust files and
117 // parse them, so we ignore them here.
118 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700119 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400120 continue 'items;
121 }
122 }
123
124 // Lookup any #[cfg()] attributes on the module and add them to
125 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800126 //
127 // The derive module is weird because it is built with either
128 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700129 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800130 quote!(#[cfg(feature = "derive")])
131 } else {
132 get_features(&item.attrs, features.clone())
133 };
Nika Layzell27726662017-10-24 23:16:35 -0400134
135 // Look up the submodule file, and recursively parse it.
136 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700137 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500138 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400139 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500140 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400141 // Lookip any #[cfg()] attributes directly on the macro
142 // invocation, and add them to the feature set.
143 let features = get_features(&item.attrs, features.clone());
144
145 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800146 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700147 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700148 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500149 .map_err(|_| err_msg("failed to parse ast_struct"))?
150 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700151 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700152 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500153 .map_err(|_| err_msg("failed to parse ast_enum"))?
154 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700155 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700156 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500157 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500158 .0
Nika Layzell27726662017-10-24 23:16:35 -0400159 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500160 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400161 };
162
163 // Record our features on the parsed AstItems.
164 for mut item in found {
165 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700166 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400167 }
168 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500169 Item::Struct(item) => {
170 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700171 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700172 lookup.insert(
173 ident.clone(),
174 AstItem {
175 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700176 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700177 vis: item.vis,
178 attrs: item.attrs,
179 generics: item.generics,
180 data: Data::Struct(DataStruct {
181 fields: item.fields,
182 struct_token: item.struct_token,
183 semi_token: item.semi_token,
184 }),
185 },
186 features: features.clone(),
187 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500188 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700189 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500190 }
191 }
Nika Layzell27726662017-10-24 23:16:35 -0400192 _ => {}
193 }
194 }
195 Ok(())
196}
197
198mod parsing {
199 use super::AstItem;
200
David Tolnay01ed0ce2018-05-20 20:18:14 -0700201 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500202 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700203 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400204 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400205
David Tolnay64f03842018-08-30 21:34:40 -0700206 fn peek_tag(input: ParseStream, tag: &str) -> bool {
207 let ahead = input.fork();
David Tolnay86cb9452018-08-31 10:35:09 -0700208 ahead.parse::<Token![#]>().is_ok() && ahead
209 .parse::<Ident>()
210 .map(|ident| ident == tag)
211 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700212 }
213
Nika Layzell27726662017-10-24 23:16:35 -0400214 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
215 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700216 fn full(input: ParseStream) -> (TokenStream, bool) {
217 if peek_tag(input, "full") {
218 input.parse::<Token![#]>().unwrap();
219 input.parse::<Ident>().unwrap();
220 (quote!(#[cfg(feature = "full")]), true)
221 } else {
222 (quote!(), false)
223 }
224 }
Nika Layzell27726662017-10-24 23:16:35 -0400225
David Tolnay64f03842018-08-30 21:34:40 -0700226 fn skip_manual_extra_traits(input: ParseStream) {
227 if peek_tag(input, "manual_extra_traits") {
228 input.parse::<Token![#]>().unwrap();
229 input.parse::<Ident>().unwrap();
230 }
231 }
David Tolnay28c5a462017-12-27 01:59:30 -0500232
Nika Layzell27726662017-10-24 23:16:35 -0400233 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700234 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
235 let ident: Ident = input.parse()?;
236 let (features, eos_full) = full(input);
237 skip_manual_extra_traits(input);
238 let rest: TokenStream = input.parse()?;
239 Ok(AstItem {
240 ast: syn::parse2(quote! {
241 pub struct #ident #rest
242 })?,
243 features: features,
244 eos_full: eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400245 })
David Tolnay64f03842018-08-30 21:34:40 -0700246 }
Nika Layzell27726662017-10-24 23:16:35 -0400247
248 // ast_struct! parsing
249 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700250 impl Parse for AstStruct {
251 fn parse(input: ParseStream) -> Result<Self> {
252 input.call(Attribute::parse_outer)?;
253 input.parse::<Token![pub]>()?;
254 input.parse::<Token![struct]>()?;
255 let res = input.call(ast_struct_inner)?;
256 Ok(AstStruct(vec![res]))
257 }
Nika Layzell27726662017-10-24 23:16:35 -0400258 }
259
David Tolnay64f03842018-08-30 21:34:40 -0700260 fn no_visit(input: ParseStream) -> bool {
261 if peek_tag(input, "no_visit") {
262 input.parse::<Token![#]>().unwrap();
263 input.parse::<Ident>().unwrap();
264 true
265 } else {
266 false
267 }
268 }
David Tolnay360efd22018-01-04 23:35:26 -0800269
Nika Layzell27726662017-10-24 23:16:35 -0400270 // ast_enum! parsing
271 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700272 impl Parse for AstEnum {
273 fn parse(input: ParseStream) -> Result<Self> {
274 input.call(Attribute::parse_outer)?;
275 input.parse::<Token![pub]>()?;
276 input.parse::<Token![enum]>()?;
277 let ident: Ident = input.parse()?;
278 let no_visit = no_visit(input);
279 let rest: TokenStream = input.parse()?;
280 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800281 vec![]
282 } else {
283 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700284 ast: syn::parse2(quote! {
285 pub enum #ident #rest
286 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800287 features: quote!(),
288 eos_full: false,
289 }]
290 }))
David Tolnay64f03842018-08-30 21:34:40 -0700291 }
Nika Layzell27726662017-10-24 23:16:35 -0400292 }
293
294 // A single variant of an ast_enum_of_structs!
295 struct EosVariant {
296 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500297 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400298 inner: Option<AstItem>,
299 }
David Tolnay64f03842018-08-30 21:34:40 -0700300 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
301 input.call(Attribute::parse_outer)?;
302 input.parse::<Token![pub]>()?;
303 let variant: Ident = input.parse()?;
304 let (member, inner) = if input.peek(token::Paren) {
305 let content;
306 parenthesized!(content in input);
307 if content.fork().call(ast_struct_inner).is_ok() {
308 let item = content.call(ast_struct_inner)?;
309 (Some(Path::from(item.ast.ident.clone())), Some(item))
310 } else {
311 let path: Path = content.parse()?;
312 (Some(path), None)
313 }
314 } else {
315 (None, None)
316 };
317 input.parse::<Token![,]>()?;
318 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400319 name: variant,
David Tolnay64f03842018-08-30 21:34:40 -0700320 member: member,
321 inner: inner,
Nika Layzell27726662017-10-24 23:16:35 -0400322 })
David Tolnay64f03842018-08-30 21:34:40 -0700323 }
Nika Layzell27726662017-10-24 23:16:35 -0400324
325 // ast_enum_of_structs! parsing
326 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700327 impl Parse for AstEnumOfStructs {
328 fn parse(input: ParseStream) -> Result<Self> {
329 input.call(Attribute::parse_outer)?;
330 input.parse::<Token![pub]>()?;
331 input.parse::<Token![enum]>()?;
332 let ident: Ident = input.parse()?;
333
334 let content;
335 braced!(content in input);
336 let mut variants = Vec::new();
337 while !content.is_empty() {
338 variants.push(content.call(eos_variant)?);
339 }
340
341 if let Some(ident) = input.parse::<Option<Ident>>()? {
342 assert_eq!(ident, "do_not_generate_to_tokens");
343 }
344
345 let enum_item = {
346 let variants = variants.iter().map(|v| {
347 let name = v.name.clone();
348 match v.member {
349 Some(ref member) => quote!(#name(#member)),
350 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700351 }
David Tolnay64f03842018-08-30 21:34:40 -0700352 });
353 parse_quote! {
354 pub enum #ident {
355 #(#variants),*
356 }
357 }
358 };
359 let mut items = vec![AstItem {
360 ast: enum_item,
361 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700362 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700363 }];
364 items.extend(variants.into_iter().filter_map(|v| v.inner));
365 Ok(AstEnumOfStructs(items))
366 }
Nika Layzell27726662017-10-24 23:16:35 -0400367 }
368}
369
370mod codegen {
371 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700372 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700373 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700374 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700375 use syn::ext::IdentExt;
376 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700377 use syn::punctuated::Punctuated;
378 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400379
380 #[derive(Default)]
381 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700382 pub visit_trait: TokenStream,
383 pub visit_impl: TokenStream,
384 pub visit_mut_trait: TokenStream,
385 pub visit_mut_impl: TokenStream,
386 pub fold_trait: TokenStream,
387 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400388 }
389
David Tolnay4a918742017-12-28 16:54:41 -0500390 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700391 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400392 }
393
David Tolnay3d772182017-12-28 17:18:53 -0500394 enum RelevantType<'a> {
395 Box(&'a Type),
396 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500397 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500398 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500399 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500400 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400401 TokenPunct(TokenStream),
402 TokenKeyword(TokenStream),
403 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500404 Pass,
405 }
Nika Layzell27726662017-10-24 23:16:35 -0400406
David Tolnay3d772182017-12-28 17:18:53 -0500407 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
408 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700409 Type::Path(TypePath {
410 qself: None,
411 ref path,
412 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800413 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700414 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500415 "Box" => RelevantType::Box(first_arg(&last.arguments)),
416 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500417 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500418 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500419 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400420 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500421 }
David Tolnay3d772182017-12-28 17:18:53 -0500422 _ => {
423 if let Some(item) = lookup.get(&last.ident) {
424 RelevantType::Simple(item)
425 } else {
426 RelevantType::Pass
427 }
428 }
429 }
Nika Layzell27726662017-10-24 23:16:35 -0400430 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700431 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
432 Type::Macro(TypeMacro { ref mac })
433 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
434 {
David Tolnay86cb9452018-08-31 10:35:09 -0700435 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400436 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
437 if is_ident && !is_underscore {
438 RelevantType::TokenKeyword(mac.into_token_stream())
439 } else {
440 RelevantType::TokenPunct(mac.into_token_stream())
441 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500442 }
David Tolnay3d772182017-12-28 17:18:53 -0500443 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400444 }
445 }
446
447 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
448 enum Kind {
449 Visit,
450 VisitMut,
451 Fold,
452 }
453
David Tolnayf0d63bf2017-12-26 12:29:47 -0500454 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700455 Borrowed(TokenStream),
456 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500457 }
458
David Tolnay83db9272017-12-28 17:02:31 -0500459 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700460 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500461
David Tolnayf0d63bf2017-12-26 12:29:47 -0500462 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700463 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500464 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500465 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500466 }
467 }
468
Alex Crichton715862b2018-05-17 12:31:49 -0700469 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500470 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500471 Borrowed(ref n) => n.clone(),
472 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500473 }
474 }
475
Alex Crichton715862b2018-05-17 12:31:49 -0700476 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500477 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500478 Borrowed(ref n) => n.clone(),
479 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500480 }
481 }
482
Alex Crichton715862b2018-05-17 12:31:49 -0700483 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500484 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500485 Borrowed(ref n) => quote!(*#n),
486 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500487 }
488 }
489 }
490
Nika Layzellc08227a2017-12-04 16:30:17 -0500491 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400492 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500493 PathArguments::AngleBracketed(ref data) => data,
494 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400495 };
496
David Tolnay01ed0ce2018-05-20 20:18:14 -0700497 match **data
498 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500499 .first()
500 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800501 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500502 {
David Tolnayea9ae892017-12-26 01:44:32 -0500503 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500504 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500505 }
Nika Layzell27726662017-10-24 23:16:35 -0400506 }
507
David Tolnay6af48992018-08-01 11:16:28 -0700508 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
509 let ident = under_name(item.ast.ident.clone());
510
David Tolnay4a918742017-12-28 16:54:41 -0500511 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700512 Visit => {
513 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
514 let name = name.ref_tokens();
515 quote! {
516 _visitor.#method(#name)
517 }
518 }
519 VisitMut => {
520 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
521 let name = name.ref_mut_tokens();
522 quote! {
523 _visitor.#method(#name)
524 }
525 }
526 Fold => {
527 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
528 let name = name.owned_tokens();
529 quote! {
530 _visitor.#method(#name)
531 }
532 }
Nika Layzell27726662017-10-24 23:16:35 -0400533 }
534 }
535
David Tolnay6af48992018-08-01 11:16:28 -0700536 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500537 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500538 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500539 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700540 Fold => quote! {
541 Box::new(#res)
542 },
David Tolnay83db9272017-12-28 17:02:31 -0500543 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500544 })
Nika Layzell27726662017-10-24 23:16:35 -0400545 }
546
David Tolnay6af48992018-08-01 11:16:28 -0700547 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500548 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500549 Visit | VisitMut => Borrowed(quote!(it)),
550 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500551 };
David Tolnay39d0a202017-12-28 18:19:00 -0500552 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500553 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700554 Visit => {
555 let name = name.ref_tokens();
556 quote! {
557 for it in #name {
558 #val
559 }
560 }
561 }
562 VisitMut => {
563 let name = name.ref_mut_tokens();
564 quote! {
565 for it in #name {
566 #val
567 }
568 }
569 }
570 Fold => {
571 let name = name.owned_tokens();
572 quote! {
573 FoldHelper::lift(#name, |it| { #val })
574 }
575 }
David Tolnay3d772182017-12-28 17:18:53 -0500576 })
577 }
578
David Tolnay6eff4da2018-01-01 20:27:45 -0800579 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500580 elem: &Type,
581 lookup: &Lookup,
582 kind: Kind,
583 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700584 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500585 let operand = match kind {
586 Visit | VisitMut => Borrowed(quote!(it)),
587 Fold => Owned(quote!(it)),
588 };
David Tolnay39d0a202017-12-28 18:19:00 -0500589 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500590 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700591 Visit => {
592 let name = name.ref_tokens();
593 quote! {
594 for el in Punctuated::pairs(#name) {
595 let it = el.value();
596 #val
597 }
598 }
599 }
600 VisitMut => {
601 let name = name.ref_mut_tokens();
602 quote! {
603 for mut el in Punctuated::pairs_mut(#name) {
604 let it = el.value_mut();
605 #val
606 }
607 }
608 }
609 Fold => {
610 let name = name.owned_tokens();
611 quote! {
612 FoldHelper::lift(#name, |it| { #val })
613 }
614 }
David Tolnay4a918742017-12-28 16:54:41 -0500615 })
Nika Layzell27726662017-10-24 23:16:35 -0400616 }
617
David Tolnay6af48992018-08-01 11:16:28 -0700618 fn option_visit(
619 elem: &Type,
620 lookup: &Lookup,
621 kind: Kind,
622 name: &Operand,
623 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500624 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500625 Visit | VisitMut => Borrowed(quote!(it)),
626 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500627 };
David Tolnay39d0a202017-12-28 18:19:00 -0500628 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700629 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500630 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700631 Visit => quote! {
632 if let Some(ref it) = #name {
633 #val
634 }
635 },
636 VisitMut => quote! {
637 if let Some(ref mut it) = #name {
638 #val
639 }
640 },
641 Fold => quote! {
642 (#name).map(|it| { #val })
643 },
David Tolnay4a918742017-12-28 16:54:41 -0500644 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400645 }
646
David Tolnay5c4c0b52017-12-28 17:58:54 -0500647 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500648 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500649 lookup: &Lookup,
650 kind: Kind,
651 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700652 ) -> Option<TokenStream> {
653 if elems.is_empty() {
654 return None;
655 }
656
657 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500658 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500659 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500660 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500661 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700662 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700663 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500664 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700665 Fold => code.append_all(quote!(,)),
666 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500667 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500668 }
David Tolnay6af48992018-08-01 11:16:28 -0700669 Some(match kind {
670 Fold => quote! {
671 (#code)
672 },
673 Visit | VisitMut => code,
674 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500675 }
676
David Tolnay7ac699c2018-08-24 14:00:58 -0400677 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700678 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500679 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700680 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400681 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700682 },
683 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400684 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700685 },
686 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400687 tokens_helper(_visitor, &mut #name.spans)
688 },
689 }
690 }
691
692 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
693 let name = name.tokens();
694 match kind {
695 Fold => quote! {
696 #ty(tokens_helper(_visitor, &#name.span))
697 },
698 Visit => quote! {
699 tokens_helper(_visitor, &#name.span)
700 },
701 VisitMut => quote! {
702 tokens_helper(_visitor, &mut #name.span)
703 },
704 }
705 }
706
707 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
708 let name = name.tokens();
709 match kind {
710 Fold => quote! {
711 #ty(tokens_helper(_visitor, &#name.span))
712 },
713 Visit => quote! {
714 tokens_helper(_visitor, &#name.span)
715 },
716 VisitMut => quote! {
717 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700718 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500719 }
720 }
721
David Tolnay6af48992018-08-01 11:16:28 -0700722 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500723 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700724 Fold => name.owned_tokens(),
725 Visit | VisitMut => {
726 let name = name.tokens();
727 quote! {
728 skip!(#name)
729 }
730 }
David Tolnay4a918742017-12-28 16:54:41 -0500731 }
732 }
733
David Tolnay6af48992018-08-01 11:16:28 -0700734 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500735 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700736 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
737 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
738 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
739 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
740 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500741 RelevantType::Simple(item) => {
742 let mut res = simple_visit(item, kind, name);
743 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700744 quote! {
745 full!(#res)
746 }
David Tolnay3d772182017-12-28 17:18:53 -0500747 } else {
748 res
749 })
750 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400751 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
752 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
753 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700754 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400755 }
Nika Layzell27726662017-10-24 23:16:35 -0400756 }
757
758 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700759 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700760 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700761 let ty = &s.ast.ident;
762 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
763 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
764 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400765
David Tolnay6af48992018-08-01 11:16:28 -0700766 let mut visit_impl = TokenStream::new();
767 let mut visit_mut_impl = TokenStream::new();
768 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400769
David Tolnaye3d41b72017-12-31 15:24:00 -0500770 match s.ast.data {
771 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700772 let mut visit_variants = TokenStream::new();
773 let mut visit_mut_variants = TokenStream::new();
774 let mut fold_variants = TokenStream::new();
775
Nika Layzell27726662017-10-24 23:16:35 -0400776 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700777 let variant_ident = &variant.ident;
778
779 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500780 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
781 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700782 let mut bind_visit_fields = TokenStream::new();
783 let mut bind_visit_mut_fields = TokenStream::new();
784 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400785
David Tolnay6af48992018-08-01 11:16:28 -0700786 let mut visit_fields = TokenStream::new();
787 let mut visit_mut_fields = TokenStream::new();
788 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400789
David Tolnay6af48992018-08-01 11:16:28 -0700790 for (idx, field) in fields.unnamed.iter().enumerate() {
791 let name = format!("_binding_{}", idx);
792 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400793
David Tolnay6af48992018-08-01 11:16:28 -0700794 bind_visit_fields.append_all(quote! {
795 ref #binding,
796 });
797 bind_visit_mut_fields.append_all(quote! {
798 ref mut #binding,
799 });
800 bind_fold_fields.append_all(quote! {
801 #binding,
802 });
Nika Layzell27726662017-10-24 23:16:35 -0400803
David Tolnay6af48992018-08-01 11:16:28 -0700804 let borrowed_binding = Borrowed(quote!(#binding));
805 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400806
David Tolnay6af48992018-08-01 11:16:28 -0700807 visit_fields.append_all(
808 visit(&field.ty, lookup, Visit, &borrowed_binding)
809 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
810 );
811 visit_mut_fields.append_all(
812 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
813 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
814 );
815 fold_fields.append_all(
816 visit(&field.ty, lookup, Fold, &owned_binding)
817 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
818 );
Nika Layzell27726662017-10-24 23:16:35 -0400819
David Tolnay6af48992018-08-01 11:16:28 -0700820 visit_fields.append_all(quote!(;));
821 visit_mut_fields.append_all(quote!(;));
822 fold_fields.append_all(quote!(,));
823 }
Nika Layzell27726662017-10-24 23:16:35 -0400824
David Tolnay6af48992018-08-01 11:16:28 -0700825 visit_variants.append_all(quote! {
826 #ty::#variant_ident(#bind_visit_fields) => {
827 #visit_fields
828 }
829 });
830
831 visit_mut_variants.append_all(quote! {
832 #ty::#variant_ident(#bind_visit_mut_fields) => {
833 #visit_mut_fields
834 }
835 });
836
837 fold_variants.append_all(quote! {
838 #ty::#variant_ident(#bind_fold_fields) => {
839 #ty::#variant_ident(
840 #fold_fields
841 )
842 }
843 });
Nika Layzell27726662017-10-24 23:16:35 -0400844 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500845 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700846 visit_variants.append_all(quote! {
847 #ty::#variant_ident => {}
848 });
849 visit_mut_variants.append_all(quote! {
850 #ty::#variant_ident => {}
851 });
852 fold_variants.append_all(quote! {
853 #ty::#variant_ident => {
854 #ty::#variant_ident
855 }
856 });
Nika Layzell27726662017-10-24 23:16:35 -0400857 }
Nika Layzell27726662017-10-24 23:16:35 -0400858 }
Nika Layzell27726662017-10-24 23:16:35 -0400859 }
David Tolnay6af48992018-08-01 11:16:28 -0700860
861 visit_impl.append_all(quote! {
862 match *_i {
863 #visit_variants
864 }
865 });
866
867 visit_mut_impl.append_all(quote! {
868 match *_i {
869 #visit_mut_variants
870 }
871 });
872
873 fold_impl.append_all(quote! {
874 match _i {
875 #fold_variants
876 }
877 });
Nika Layzell27726662017-10-24 23:16:35 -0400878 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500879 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700880 let mut fold_fields = TokenStream::new();
881
David Tolnaybc5b2c02018-08-02 00:18:23 -0700882 for (idx, field) in v.fields.iter().enumerate() {
883 let id = match field.ident {
884 Some(ref ident) => Member::Named(ident.clone()),
885 None => Member::Unnamed(Index::from(idx)),
886 };
887 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700888 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
889 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
890 visit_impl.append_all(quote! {
891 #visit_field;
892 });
893 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
894 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
895 visit_mut_impl.append_all(quote! {
896 #visit_mut_field;
897 });
David Tolnay83db9272017-12-28 17:02:31 -0500898 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700899 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400900 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700901 fold_fields.append_all(quote! {
902 #name: #fold,
903 });
Nika Layzell27726662017-10-24 23:16:35 -0400904 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700905 fold_fields.append_all(quote! {
906 #fold,
907 });
Nika Layzell27726662017-10-24 23:16:35 -0400908 }
909 }
910
David Tolnaye3d41b72017-12-31 15:24:00 -0500911 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700912 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
913 #ty {
914 #fold_fields
915 }
916 }),
917 Fields::Unit => {
918 if ty == "Ident" {
919 fold_impl.append_all(quote! {
920 let mut _i = _i;
921 let span = _visitor.fold_span(_i.span());
922 _i.set_span(span);
923 });
924 }
925 fold_impl.append_all(quote! {
926 _i
927 });
928 }
Nika Layzell27726662017-10-24 23:16:35 -0400929 };
930 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500931 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400932 }
933
David Tolnay6af48992018-08-01 11:16:28 -0700934 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800935 if let Data::Struct(ref data) = s.ast.data {
936 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700937 if fields
938 .named
939 .iter()
940 .any(|field| field.vis == Visibility::Inherited)
941 {
David Tolnay360efd22018-01-04 23:35:26 -0800942 // Discard the generated impl if there are private fields.
943 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700944 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800945 }
946 }
David Tolnayd0adf522017-12-29 01:30:07 -0500947 }
David Tolnay6af48992018-08-01 11:16:28 -0700948
949 state.visit_trait.append_all(quote! {
950 #features
951 fn #visit_fn(&mut self, i: &'ast #ty) {
952 #visit_fn(self, i)
953 }
954 });
955
956 state.visit_impl.append_all(quote! {
957 #features
958 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
959 _visitor: &mut V, _i: &'ast #ty
960 ) {
961 #visit_impl
962 }
963 });
964
965 state.visit_mut_trait.append_all(quote! {
966 #features
967 fn #visit_mut_fn(&mut self, i: &mut #ty) {
968 #visit_mut_fn(self, i)
969 }
970 });
971
972 state.visit_mut_impl.append_all(quote! {
973 #features
974 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
975 _visitor: &mut V, _i: &mut #ty
976 ) {
977 #visit_mut_impl
978 }
979 });
980
981 state.fold_trait.append_all(quote! {
982 #features
983 fn #fold_fn(&mut self, i: #ty) -> #ty {
984 #fold_fn(self, i)
985 }
986 });
987
988 if include_fold_impl {
989 state.fold_impl.append_all(quote! {
990 #features
991 pub fn #fold_fn<V: Fold + ?Sized>(
992 _visitor: &mut V, _i: #ty
993 ) -> #ty {
994 #fold_impl
995 }
996 });
997 }
Nika Layzell27726662017-10-24 23:16:35 -0400998 }
999}
1000
David Tolnay6af48992018-08-01 11:16:28 -07001001fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -07001002 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -07001003 write!(
1004 file,
1005 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
1006 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001007 let mut config = rustfmt::Config::default();
1008 config.set().emit_mode(rustfmt::EmitMode::Stdout);
1009 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -07001010 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001011 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001012 session
1013 .format(rustfmt::Input::Text(content.to_string()))
1014 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001015}
1016
Nika Layzell27726662017-10-24 23:16:35 -04001017fn main() {
1018 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001019 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001020
Nika Layzellefb83ba2017-12-19 18:23:55 -05001021 // Load in any terminal types
1022 for &tt in TERMINAL_TYPES {
1023 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001024 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001025 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001026 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001027 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001028 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001029 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001030 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001031 }),
1032 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001033 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001034 data: Data::Struct(DataStruct {
1035 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001036 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001037 semi_token: None,
1038 }),
1039 },
hcplaa511792018-05-29 07:13:01 +03001040 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001041 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001042 },
David Tolnayd67fb752017-12-27 13:50:29 -05001043 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001044 }
1045
David Tolnay6af48992018-08-01 11:16:28 -07001046 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001047 for s in lookup.values() {
1048 codegen::generate(&mut state, &lookup, s);
1049 }
1050
David Tolnay6af48992018-08-01 11:16:28 -07001051 let full_macro = quote! {
1052 #[cfg(feature = "full")]
1053 macro_rules! full {
1054 ($e:expr) => {
1055 $e
1056 };
1057 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001058
David Tolnay6af48992018-08-01 11:16:28 -07001059 #[cfg(all(feature = "derive", not(feature = "full")))]
1060 macro_rules! full {
1061 ($e:expr) => {
1062 unreachable!()
1063 };
1064 }
1065 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001066
David Tolnay6af48992018-08-01 11:16:28 -07001067 let skip_macro = quote! {
1068 #[cfg(any(feature = "full", feature = "derive"))]
1069 macro_rules! skip {
1070 ($($tt:tt)*) => {};
1071 }
1072 };
1073
1074 let fold_trait = state.fold_trait;
1075 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001076 write_file(
1077 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001078 quote! {
1079 // Unreachable code is generated sometimes without the full feature.
1080 #![allow(unreachable_code)]
1081 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001082
David Tolnay6af48992018-08-01 11:16:28 -07001083 use *;
1084 #[cfg(any(feature = "full", feature = "derive"))]
1085 use token::{Brace, Bracket, Paren, Group};
1086 use proc_macro2::Span;
1087 #[cfg(any(feature = "full", feature = "derive"))]
1088 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001089
David Tolnay6af48992018-08-01 11:16:28 -07001090 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001091
David Tolnay6af48992018-08-01 11:16:28 -07001092 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1093 ///
1094 /// See the [module documentation] for details.
1095 ///
1096 /// [module documentation]: index.html
1097 ///
1098 /// *This trait is available if Syn is built with the `"fold"` feature.*
1099 pub trait Fold {
1100 #fold_trait
1101 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001102
David Tolnay6af48992018-08-01 11:16:28 -07001103 #[cfg(any(feature = "full", feature = "derive"))]
1104 macro_rules! fold_span_only {
1105 ($f:ident : $t:ident) => {
1106 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1107 let span = _visitor.fold_span(_i.span());
1108 _i.set_span(span);
1109 _i
1110 }
1111 }
1112 }
Nika Layzell27726662017-10-24 23:16:35 -04001113
David Tolnay6af48992018-08-01 11:16:28 -07001114 #[cfg(any(feature = "full", feature = "derive"))]
1115 fold_span_only!(fold_lit_byte: LitByte);
1116 #[cfg(any(feature = "full", feature = "derive"))]
1117 fold_span_only!(fold_lit_byte_str: LitByteStr);
1118 #[cfg(any(feature = "full", feature = "derive"))]
1119 fold_span_only!(fold_lit_char: LitChar);
1120 #[cfg(any(feature = "full", feature = "derive"))]
1121 fold_span_only!(fold_lit_float: LitFloat);
1122 #[cfg(any(feature = "full", feature = "derive"))]
1123 fold_span_only!(fold_lit_int: LitInt);
1124 #[cfg(any(feature = "full", feature = "derive"))]
1125 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001126
David Tolnay6af48992018-08-01 11:16:28 -07001127 #fold_impl
1128 },
David Tolnayae0009e2018-08-01 00:40:00 -07001129 );
Nika Layzell27726662017-10-24 23:16:35 -04001130
David Tolnay6af48992018-08-01 11:16:28 -07001131 let visit_trait = state.visit_trait;
1132 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001133 write_file(
1134 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001135 quote! {
1136 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001137
David Tolnay6af48992018-08-01 11:16:28 -07001138 use *;
1139 #[cfg(any(feature = "full", feature = "derive"))]
1140 use punctuated::Punctuated;
1141 use proc_macro2::Span;
1142 #[cfg(any(feature = "full", feature = "derive"))]
1143 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001144
David Tolnay6af48992018-08-01 11:16:28 -07001145 #full_macro
1146 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001147
David Tolnay6af48992018-08-01 11:16:28 -07001148 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1149 ///
1150 /// See the [module documentation] for details.
1151 ///
1152 /// [module documentation]: index.html
1153 ///
1154 /// *This trait is available if Syn is built with the `"visit"` feature.*
1155 pub trait Visit<'ast> {
1156 #visit_trait
1157 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001158
David Tolnay6af48992018-08-01 11:16:28 -07001159 #visit_impl
1160 },
David Tolnayae0009e2018-08-01 00:40:00 -07001161 );
Nika Layzell27726662017-10-24 23:16:35 -04001162
David Tolnay6af48992018-08-01 11:16:28 -07001163 let visit_mut_trait = state.visit_mut_trait;
1164 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001165 write_file(
1166 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001167 quote! {
1168 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001169
David Tolnay6af48992018-08-01 11:16:28 -07001170 use *;
1171 #[cfg(any(feature = "full", feature = "derive"))]
1172 use punctuated::Punctuated;
1173 use proc_macro2::Span;
1174 #[cfg(any(feature = "full", feature = "derive"))]
1175 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001176
David Tolnay6af48992018-08-01 11:16:28 -07001177 #full_macro
1178 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001179
David Tolnay6af48992018-08-01 11:16:28 -07001180 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1181 /// place.
1182 ///
1183 /// See the [module documentation] for details.
1184 ///
1185 /// [module documentation]: index.html
1186 ///
1187 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1188 pub trait VisitMut {
1189 #visit_mut_trait
1190 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001191
David Tolnay6af48992018-08-01 11:16:28 -07001192 #visit_mut_impl
1193 },
David Tolnayae0009e2018-08-01 00:40:00 -07001194 );
Nika Layzell27726662017-10-24 23:16:35 -04001195}