blob: c4c8b8f362525d91a3fb2e800d86f692a8d03286 [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 Tolnayc1f55792018-11-21 01:39:42 -080014#![allow(clippy::needless_pass_by_value)]
David Tolnayea9ae892017-12-26 01:44:32 -050015
David Tolnayd67fb752017-12-27 13:50:29 -050016#[macro_use]
17extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040018extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050019extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050020#[macro_use]
21extern crate quote;
22extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070023extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040024
David Tolnayd67fb752017-12-27 13:50:29 -050025use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070026use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070027use quote::ToTokens;
28use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040029
David Tolnay01ed0ce2018-05-20 20:18:14 -070030use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050031use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040032use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070033use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040034use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040035
36const SYN_CRATE_ROOT: &str = "../src/lib.rs";
37
38const FOLD_SRC: &str = "../src/gen/fold.rs";
39const VISIT_SRC: &str = "../src/gen/visit.rs";
40const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
41
David Tolnayd67fb752017-12-27 13:50:29 -050042const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040043
Alex Crichton131308c2018-05-18 14:00:24 -070044const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050045
Alex Crichtond261d092018-05-18 13:47:35 -070046const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050047
Alex Crichtona74a1c82018-05-16 10:20:44 -070048fn path_eq(a: &syn::Path, b: &str) -> bool {
David Tolnay78b16142018-09-01 16:53:07 -070049 a.leading_colon.is_none()
50 && a.segments.len() == 1
51 && a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040052}
53
Alex Crichton715862b2018-05-17 12:31:49 -070054fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040055 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070056 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040057 attr.to_tokens(&mut features);
58 }
59 }
60 features
61}
62
63#[derive(Clone)]
64pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050065 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070066 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040067 // True if this is an ast_enum_of_structs! item with a #full annotation.
68 eos_full: bool,
69}
70
David Tolnayf0d63bf2017-12-26 12:29:47 -050071impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040072 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050074 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040075 .field("features", &self.features.to_string())
76 .finish()
77 }
78}
79
80// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050081type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040082
David Tolnay01ed0ce2018-05-20 20:18:14 -070083fn load_file<P: AsRef<Path>>(
84 name: P,
85 features: &TokenStream,
86 lookup: &mut Lookup,
87) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040088 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050089 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040090
91 let mut f = File::open(name)?;
92 let mut src = String::new();
93 f.read_to_string(&mut src)?;
94
95 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050096 let file =
97 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -040098
99 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500100 'items: for item in file.items {
101 match item {
102 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400103 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800104 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400105 continue;
106 }
107
108 // We don't want to try to load the generated rust files and
109 // parse them, so we ignore them here.
110 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700111 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400112 continue 'items;
113 }
114 }
115
116 // Lookup any #[cfg()] attributes on the module and add them to
117 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800118 //
119 // The derive module is weird because it is built with either
120 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700121 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800122 quote!(#[cfg(feature = "derive")])
123 } else {
124 get_features(&item.attrs, features.clone())
125 };
Nika Layzell27726662017-10-24 23:16:35 -0400126
127 // Look up the submodule file, and recursively parse it.
128 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700129 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500130 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400131 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500132 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400133 // Lookip any #[cfg()] attributes directly on the macro
134 // invocation, and add them to the feature set.
135 let features = get_features(&item.attrs, features.clone());
136
137 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800138 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700139 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700140 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500141 .map_err(|_| err_msg("failed to parse ast_struct"))?
142 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700143 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700144 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500145 .map_err(|_| err_msg("failed to parse ast_enum"))?
146 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700147 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700148 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500149 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500150 .0
Nika Layzell27726662017-10-24 23:16:35 -0400151 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500152 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400153 };
154
155 // Record our features on the parsed AstItems.
156 for mut item in found {
157 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700158 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400159 }
160 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500161 Item::Struct(item) => {
162 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700163 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700164 lookup.insert(
165 ident.clone(),
166 AstItem {
167 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700168 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700169 vis: item.vis,
170 attrs: item.attrs,
171 generics: item.generics,
172 data: Data::Struct(DataStruct {
173 fields: item.fields,
174 struct_token: item.struct_token,
175 semi_token: item.semi_token,
176 }),
177 },
178 features: features.clone(),
179 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500180 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700181 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500182 }
183 }
Nika Layzell27726662017-10-24 23:16:35 -0400184 _ => {}
185 }
186 }
187 Ok(())
188}
189
190mod parsing {
191 use super::AstItem;
192
David Tolnay01ed0ce2018-05-20 20:18:14 -0700193 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500194 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700195 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400196 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400197
David Tolnay64f03842018-08-30 21:34:40 -0700198 fn peek_tag(input: ParseStream, tag: &str) -> bool {
199 let ahead = input.fork();
David Tolnay86cb9452018-08-31 10:35:09 -0700200 ahead.parse::<Token![#]>().is_ok() && ahead
201 .parse::<Ident>()
202 .map(|ident| ident == tag)
203 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700204 }
205
Nika Layzell27726662017-10-24 23:16:35 -0400206 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
207 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700208 fn full(input: ParseStream) -> (TokenStream, bool) {
209 if peek_tag(input, "full") {
210 input.parse::<Token![#]>().unwrap();
211 input.parse::<Ident>().unwrap();
212 (quote!(#[cfg(feature = "full")]), true)
213 } else {
214 (quote!(), false)
215 }
216 }
Nika Layzell27726662017-10-24 23:16:35 -0400217
David Tolnay64f03842018-08-30 21:34:40 -0700218 fn skip_manual_extra_traits(input: ParseStream) {
219 if peek_tag(input, "manual_extra_traits") {
220 input.parse::<Token![#]>().unwrap();
221 input.parse::<Ident>().unwrap();
222 }
223 }
David Tolnay28c5a462017-12-27 01:59:30 -0500224
Nika Layzell27726662017-10-24 23:16:35 -0400225 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700226 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
227 let ident: Ident = input.parse()?;
228 let (features, eos_full) = full(input);
229 skip_manual_extra_traits(input);
230 let rest: TokenStream = input.parse()?;
231 Ok(AstItem {
232 ast: syn::parse2(quote! {
233 pub struct #ident #rest
234 })?,
David Tolnay9a518142018-11-21 01:38:50 -0800235 features,
236 eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400237 })
David Tolnay64f03842018-08-30 21:34:40 -0700238 }
Nika Layzell27726662017-10-24 23:16:35 -0400239
240 // ast_struct! parsing
241 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700242 impl Parse for AstStruct {
243 fn parse(input: ParseStream) -> Result<Self> {
244 input.call(Attribute::parse_outer)?;
245 input.parse::<Token![pub]>()?;
246 input.parse::<Token![struct]>()?;
247 let res = input.call(ast_struct_inner)?;
248 Ok(AstStruct(vec![res]))
249 }
Nika Layzell27726662017-10-24 23:16:35 -0400250 }
251
David Tolnay64f03842018-08-30 21:34:40 -0700252 fn no_visit(input: ParseStream) -> bool {
253 if peek_tag(input, "no_visit") {
254 input.parse::<Token![#]>().unwrap();
255 input.parse::<Ident>().unwrap();
256 true
257 } else {
258 false
259 }
260 }
David Tolnay360efd22018-01-04 23:35:26 -0800261
Nika Layzell27726662017-10-24 23:16:35 -0400262 // ast_enum! parsing
263 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700264 impl Parse for AstEnum {
265 fn parse(input: ParseStream) -> Result<Self> {
266 input.call(Attribute::parse_outer)?;
267 input.parse::<Token![pub]>()?;
268 input.parse::<Token![enum]>()?;
269 let ident: Ident = input.parse()?;
270 let no_visit = no_visit(input);
271 let rest: TokenStream = input.parse()?;
272 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800273 vec![]
274 } else {
275 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700276 ast: syn::parse2(quote! {
277 pub enum #ident #rest
278 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800279 features: quote!(),
280 eos_full: false,
281 }]
282 }))
David Tolnay64f03842018-08-30 21:34:40 -0700283 }
Nika Layzell27726662017-10-24 23:16:35 -0400284 }
285
286 // A single variant of an ast_enum_of_structs!
287 struct EosVariant {
288 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500289 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400290 inner: Option<AstItem>,
291 }
David Tolnay64f03842018-08-30 21:34:40 -0700292 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
293 input.call(Attribute::parse_outer)?;
294 input.parse::<Token![pub]>()?;
295 let variant: Ident = input.parse()?;
296 let (member, inner) = if input.peek(token::Paren) {
297 let content;
298 parenthesized!(content in input);
299 if content.fork().call(ast_struct_inner).is_ok() {
300 let item = content.call(ast_struct_inner)?;
301 (Some(Path::from(item.ast.ident.clone())), Some(item))
302 } else {
303 let path: Path = content.parse()?;
304 (Some(path), None)
305 }
306 } else {
307 (None, None)
308 };
309 input.parse::<Token![,]>()?;
310 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400311 name: variant,
David Tolnay9a518142018-11-21 01:38:50 -0800312 member,
313 inner,
Nika Layzell27726662017-10-24 23:16:35 -0400314 })
David Tolnay64f03842018-08-30 21:34:40 -0700315 }
Nika Layzell27726662017-10-24 23:16:35 -0400316
317 // ast_enum_of_structs! parsing
318 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700319 impl Parse for AstEnumOfStructs {
320 fn parse(input: ParseStream) -> Result<Self> {
321 input.call(Attribute::parse_outer)?;
322 input.parse::<Token![pub]>()?;
323 input.parse::<Token![enum]>()?;
324 let ident: Ident = input.parse()?;
325
326 let content;
327 braced!(content in input);
328 let mut variants = Vec::new();
329 while !content.is_empty() {
330 variants.push(content.call(eos_variant)?);
331 }
332
333 if let Some(ident) = input.parse::<Option<Ident>>()? {
334 assert_eq!(ident, "do_not_generate_to_tokens");
335 }
336
337 let enum_item = {
338 let variants = variants.iter().map(|v| {
339 let name = v.name.clone();
340 match v.member {
341 Some(ref member) => quote!(#name(#member)),
342 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700343 }
David Tolnay64f03842018-08-30 21:34:40 -0700344 });
345 parse_quote! {
346 pub enum #ident {
347 #(#variants),*
348 }
349 }
350 };
351 let mut items = vec![AstItem {
352 ast: enum_item,
353 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700354 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700355 }];
356 items.extend(variants.into_iter().filter_map(|v| v.inner));
357 Ok(AstEnumOfStructs(items))
358 }
Nika Layzell27726662017-10-24 23:16:35 -0400359 }
360}
361
362mod codegen {
363 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700364 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700365 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700366 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700367 use syn::ext::IdentExt;
368 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700369 use syn::punctuated::Punctuated;
370 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400371
372 #[derive(Default)]
373 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700374 pub visit_trait: TokenStream,
375 pub visit_impl: TokenStream,
376 pub visit_mut_trait: TokenStream,
377 pub visit_mut_impl: TokenStream,
378 pub fold_trait: TokenStream,
379 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400380 }
381
David Tolnay4a918742017-12-28 16:54:41 -0500382 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700383 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400384 }
385
David Tolnay3d772182017-12-28 17:18:53 -0500386 enum RelevantType<'a> {
387 Box(&'a Type),
388 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500389 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500390 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500391 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500392 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400393 TokenPunct(TokenStream),
394 TokenKeyword(TokenStream),
395 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500396 Pass,
397 }
Nika Layzell27726662017-10-24 23:16:35 -0400398
David Tolnay3d772182017-12-28 17:18:53 -0500399 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
400 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700401 Type::Path(TypePath {
402 qself: None,
403 ref path,
404 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800405 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700406 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500407 "Box" => RelevantType::Box(first_arg(&last.arguments)),
408 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500409 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500410 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500411 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400412 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500413 }
David Tolnay3d772182017-12-28 17:18:53 -0500414 _ => {
415 if let Some(item) = lookup.get(&last.ident) {
416 RelevantType::Simple(item)
417 } else {
418 RelevantType::Pass
419 }
420 }
421 }
Nika Layzell27726662017-10-24 23:16:35 -0400422 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700423 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
424 Type::Macro(TypeMacro { ref mac })
425 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
426 {
David Tolnay86cb9452018-08-31 10:35:09 -0700427 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400428 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
429 if is_ident && !is_underscore {
430 RelevantType::TokenKeyword(mac.into_token_stream())
431 } else {
432 RelevantType::TokenPunct(mac.into_token_stream())
433 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500434 }
David Tolnay3d772182017-12-28 17:18:53 -0500435 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400436 }
437 }
438
439 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
440 enum Kind {
441 Visit,
442 VisitMut,
443 Fold,
444 }
445
David Tolnayf0d63bf2017-12-26 12:29:47 -0500446 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700447 Borrowed(TokenStream),
448 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500449 }
450
David Tolnay83db9272017-12-28 17:02:31 -0500451 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700452 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500453
David Tolnayf0d63bf2017-12-26 12:29:47 -0500454 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700455 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500456 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500457 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500458 }
459 }
460
Alex Crichton715862b2018-05-17 12:31:49 -0700461 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500462 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500463 Borrowed(ref n) => n.clone(),
464 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500465 }
466 }
467
Alex Crichton715862b2018-05-17 12:31:49 -0700468 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500469 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500470 Borrowed(ref n) => n.clone(),
471 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500472 }
473 }
474
Alex Crichton715862b2018-05-17 12:31:49 -0700475 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500476 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500477 Borrowed(ref n) => quote!(*#n),
478 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500479 }
480 }
481 }
482
Nika Layzellc08227a2017-12-04 16:30:17 -0500483 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400484 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500485 PathArguments::AngleBracketed(ref data) => data,
486 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400487 };
488
David Tolnay01ed0ce2018-05-20 20:18:14 -0700489 match **data
490 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500491 .first()
492 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800493 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500494 {
David Tolnayea9ae892017-12-26 01:44:32 -0500495 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500496 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500497 }
Nika Layzell27726662017-10-24 23:16:35 -0400498 }
499
David Tolnay6af48992018-08-01 11:16:28 -0700500 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
501 let ident = under_name(item.ast.ident.clone());
502
David Tolnay4a918742017-12-28 16:54:41 -0500503 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700504 Visit => {
505 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
506 let name = name.ref_tokens();
507 quote! {
508 _visitor.#method(#name)
509 }
510 }
511 VisitMut => {
512 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
513 let name = name.ref_mut_tokens();
514 quote! {
515 _visitor.#method(#name)
516 }
517 }
518 Fold => {
519 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
520 let name = name.owned_tokens();
521 quote! {
522 _visitor.#method(#name)
523 }
524 }
Nika Layzell27726662017-10-24 23:16:35 -0400525 }
526 }
527
David Tolnay6af48992018-08-01 11:16:28 -0700528 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500529 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500530 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500531 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700532 Fold => quote! {
533 Box::new(#res)
534 },
David Tolnay83db9272017-12-28 17:02:31 -0500535 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500536 })
Nika Layzell27726662017-10-24 23:16:35 -0400537 }
538
David Tolnay6af48992018-08-01 11:16:28 -0700539 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500540 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500541 Visit | VisitMut => Borrowed(quote!(it)),
542 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500543 };
David Tolnay39d0a202017-12-28 18:19:00 -0500544 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500545 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700546 Visit => {
547 let name = name.ref_tokens();
548 quote! {
549 for it in #name {
550 #val
551 }
552 }
553 }
554 VisitMut => {
555 let name = name.ref_mut_tokens();
556 quote! {
557 for it in #name {
558 #val
559 }
560 }
561 }
562 Fold => {
563 let name = name.owned_tokens();
564 quote! {
565 FoldHelper::lift(#name, |it| { #val })
566 }
567 }
David Tolnay3d772182017-12-28 17:18:53 -0500568 })
569 }
570
David Tolnay6eff4da2018-01-01 20:27:45 -0800571 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500572 elem: &Type,
573 lookup: &Lookup,
574 kind: Kind,
575 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700576 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500577 let operand = match kind {
578 Visit | VisitMut => Borrowed(quote!(it)),
579 Fold => Owned(quote!(it)),
580 };
David Tolnay39d0a202017-12-28 18:19:00 -0500581 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500582 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700583 Visit => {
584 let name = name.ref_tokens();
585 quote! {
586 for el in Punctuated::pairs(#name) {
587 let it = el.value();
588 #val
589 }
590 }
591 }
592 VisitMut => {
593 let name = name.ref_mut_tokens();
594 quote! {
595 for mut el in Punctuated::pairs_mut(#name) {
596 let it = el.value_mut();
597 #val
598 }
599 }
600 }
601 Fold => {
602 let name = name.owned_tokens();
603 quote! {
604 FoldHelper::lift(#name, |it| { #val })
605 }
606 }
David Tolnay4a918742017-12-28 16:54:41 -0500607 })
Nika Layzell27726662017-10-24 23:16:35 -0400608 }
609
David Tolnay6af48992018-08-01 11:16:28 -0700610 fn option_visit(
611 elem: &Type,
612 lookup: &Lookup,
613 kind: Kind,
614 name: &Operand,
615 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500616 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500617 Visit | VisitMut => Borrowed(quote!(it)),
618 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500619 };
David Tolnay39d0a202017-12-28 18:19:00 -0500620 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700621 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500622 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700623 Visit => quote! {
624 if let Some(ref it) = #name {
625 #val
626 }
627 },
628 VisitMut => quote! {
629 if let Some(ref mut it) = #name {
630 #val
631 }
632 },
633 Fold => quote! {
634 (#name).map(|it| { #val })
635 },
David Tolnay4a918742017-12-28 16:54:41 -0500636 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400637 }
638
David Tolnay5c4c0b52017-12-28 17:58:54 -0500639 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500640 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500641 lookup: &Lookup,
642 kind: Kind,
643 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700644 ) -> Option<TokenStream> {
645 if elems.is_empty() {
646 return None;
647 }
648
649 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500650 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500651 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500652 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500653 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700654 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700655 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500656 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700657 Fold => code.append_all(quote!(,)),
658 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500659 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500660 }
David Tolnay6af48992018-08-01 11:16:28 -0700661 Some(match kind {
662 Fold => quote! {
663 (#code)
664 },
665 Visit | VisitMut => code,
666 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500667 }
668
David Tolnay7ac699c2018-08-24 14:00:58 -0400669 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700670 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500671 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700672 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400673 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700674 },
675 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400676 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700677 },
678 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400679 tokens_helper(_visitor, &mut #name.spans)
680 },
681 }
682 }
683
684 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
685 let name = name.tokens();
686 match kind {
687 Fold => quote! {
688 #ty(tokens_helper(_visitor, &#name.span))
689 },
690 Visit => quote! {
691 tokens_helper(_visitor, &#name.span)
692 },
693 VisitMut => quote! {
694 tokens_helper(_visitor, &mut #name.span)
695 },
696 }
697 }
698
699 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
700 let name = name.tokens();
701 match kind {
702 Fold => quote! {
703 #ty(tokens_helper(_visitor, &#name.span))
704 },
705 Visit => quote! {
706 tokens_helper(_visitor, &#name.span)
707 },
708 VisitMut => quote! {
709 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700710 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500711 }
712 }
713
David Tolnay6af48992018-08-01 11:16:28 -0700714 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500715 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700716 Fold => name.owned_tokens(),
717 Visit | VisitMut => {
718 let name = name.tokens();
719 quote! {
720 skip!(#name)
721 }
722 }
David Tolnay4a918742017-12-28 16:54:41 -0500723 }
724 }
725
David Tolnay6af48992018-08-01 11:16:28 -0700726 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500727 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700728 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
729 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
730 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
731 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
732 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500733 RelevantType::Simple(item) => {
734 let mut res = simple_visit(item, kind, name);
735 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700736 quote! {
737 full!(#res)
738 }
David Tolnay3d772182017-12-28 17:18:53 -0500739 } else {
740 res
741 })
742 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400743 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
744 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
745 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700746 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400747 }
Nika Layzell27726662017-10-24 23:16:35 -0400748 }
749
750 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700751 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700752 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700753 let ty = &s.ast.ident;
754 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
755 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
756 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400757
David Tolnay6af48992018-08-01 11:16:28 -0700758 let mut visit_impl = TokenStream::new();
759 let mut visit_mut_impl = TokenStream::new();
760 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400761
David Tolnaye3d41b72017-12-31 15:24:00 -0500762 match s.ast.data {
763 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700764 let mut visit_variants = TokenStream::new();
765 let mut visit_mut_variants = TokenStream::new();
766 let mut fold_variants = TokenStream::new();
767
Nika Layzell27726662017-10-24 23:16:35 -0400768 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700769 let variant_ident = &variant.ident;
770
771 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500772 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
773 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700774 let mut bind_visit_fields = TokenStream::new();
775 let mut bind_visit_mut_fields = TokenStream::new();
776 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400777
David Tolnay6af48992018-08-01 11:16:28 -0700778 let mut visit_fields = TokenStream::new();
779 let mut visit_mut_fields = TokenStream::new();
780 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400781
David Tolnay6af48992018-08-01 11:16:28 -0700782 for (idx, field) in fields.unnamed.iter().enumerate() {
783 let name = format!("_binding_{}", idx);
784 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400785
David Tolnay6af48992018-08-01 11:16:28 -0700786 bind_visit_fields.append_all(quote! {
787 ref #binding,
788 });
789 bind_visit_mut_fields.append_all(quote! {
790 ref mut #binding,
791 });
792 bind_fold_fields.append_all(quote! {
793 #binding,
794 });
Nika Layzell27726662017-10-24 23:16:35 -0400795
David Tolnay6af48992018-08-01 11:16:28 -0700796 let borrowed_binding = Borrowed(quote!(#binding));
797 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400798
David Tolnay6af48992018-08-01 11:16:28 -0700799 visit_fields.append_all(
800 visit(&field.ty, lookup, Visit, &borrowed_binding)
801 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
802 );
803 visit_mut_fields.append_all(
804 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
805 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
806 );
807 fold_fields.append_all(
808 visit(&field.ty, lookup, Fold, &owned_binding)
809 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
810 );
Nika Layzell27726662017-10-24 23:16:35 -0400811
David Tolnay6af48992018-08-01 11:16:28 -0700812 visit_fields.append_all(quote!(;));
813 visit_mut_fields.append_all(quote!(;));
814 fold_fields.append_all(quote!(,));
815 }
Nika Layzell27726662017-10-24 23:16:35 -0400816
David Tolnay6af48992018-08-01 11:16:28 -0700817 visit_variants.append_all(quote! {
818 #ty::#variant_ident(#bind_visit_fields) => {
819 #visit_fields
820 }
821 });
822
823 visit_mut_variants.append_all(quote! {
824 #ty::#variant_ident(#bind_visit_mut_fields) => {
825 #visit_mut_fields
826 }
827 });
828
829 fold_variants.append_all(quote! {
830 #ty::#variant_ident(#bind_fold_fields) => {
831 #ty::#variant_ident(
832 #fold_fields
833 )
834 }
835 });
Nika Layzell27726662017-10-24 23:16:35 -0400836 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500837 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700838 visit_variants.append_all(quote! {
839 #ty::#variant_ident => {}
840 });
841 visit_mut_variants.append_all(quote! {
842 #ty::#variant_ident => {}
843 });
844 fold_variants.append_all(quote! {
845 #ty::#variant_ident => {
846 #ty::#variant_ident
847 }
848 });
Nika Layzell27726662017-10-24 23:16:35 -0400849 }
Nika Layzell27726662017-10-24 23:16:35 -0400850 }
Nika Layzell27726662017-10-24 23:16:35 -0400851 }
David Tolnay6af48992018-08-01 11:16:28 -0700852
853 visit_impl.append_all(quote! {
854 match *_i {
855 #visit_variants
856 }
857 });
858
859 visit_mut_impl.append_all(quote! {
860 match *_i {
861 #visit_mut_variants
862 }
863 });
864
865 fold_impl.append_all(quote! {
866 match _i {
867 #fold_variants
868 }
869 });
Nika Layzell27726662017-10-24 23:16:35 -0400870 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500871 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700872 let mut fold_fields = TokenStream::new();
873
David Tolnaybc5b2c02018-08-02 00:18:23 -0700874 for (idx, field) in v.fields.iter().enumerate() {
875 let id = match field.ident {
876 Some(ref ident) => Member::Named(ident.clone()),
877 None => Member::Unnamed(Index::from(idx)),
878 };
879 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700880 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
881 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
882 visit_impl.append_all(quote! {
883 #visit_field;
884 });
885 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
886 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
887 visit_mut_impl.append_all(quote! {
888 #visit_mut_field;
889 });
David Tolnay83db9272017-12-28 17:02:31 -0500890 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700891 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400892 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700893 fold_fields.append_all(quote! {
894 #name: #fold,
895 });
Nika Layzell27726662017-10-24 23:16:35 -0400896 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700897 fold_fields.append_all(quote! {
898 #fold,
899 });
Nika Layzell27726662017-10-24 23:16:35 -0400900 }
901 }
902
David Tolnaye3d41b72017-12-31 15:24:00 -0500903 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700904 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
905 #ty {
906 #fold_fields
907 }
908 }),
909 Fields::Unit => {
910 if ty == "Ident" {
911 fold_impl.append_all(quote! {
912 let mut _i = _i;
913 let span = _visitor.fold_span(_i.span());
914 _i.set_span(span);
915 });
916 }
917 fold_impl.append_all(quote! {
918 _i
919 });
920 }
Nika Layzell27726662017-10-24 23:16:35 -0400921 };
922 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500923 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400924 }
925
David Tolnay6af48992018-08-01 11:16:28 -0700926 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800927 if let Data::Struct(ref data) = s.ast.data {
928 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700929 if fields
930 .named
931 .iter()
932 .any(|field| field.vis == Visibility::Inherited)
933 {
David Tolnay360efd22018-01-04 23:35:26 -0800934 // Discard the generated impl if there are private fields.
935 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700936 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800937 }
938 }
David Tolnayd0adf522017-12-29 01:30:07 -0500939 }
David Tolnay6af48992018-08-01 11:16:28 -0700940
941 state.visit_trait.append_all(quote! {
942 #features
943 fn #visit_fn(&mut self, i: &'ast #ty) {
944 #visit_fn(self, i)
945 }
946 });
947
948 state.visit_impl.append_all(quote! {
949 #features
950 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
951 _visitor: &mut V, _i: &'ast #ty
952 ) {
953 #visit_impl
954 }
955 });
956
957 state.visit_mut_trait.append_all(quote! {
958 #features
959 fn #visit_mut_fn(&mut self, i: &mut #ty) {
960 #visit_mut_fn(self, i)
961 }
962 });
963
964 state.visit_mut_impl.append_all(quote! {
965 #features
966 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
967 _visitor: &mut V, _i: &mut #ty
968 ) {
969 #visit_mut_impl
970 }
971 });
972
973 state.fold_trait.append_all(quote! {
974 #features
975 fn #fold_fn(&mut self, i: #ty) -> #ty {
976 #fold_fn(self, i)
977 }
978 });
979
980 if include_fold_impl {
981 state.fold_impl.append_all(quote! {
982 #features
983 pub fn #fold_fn<V: Fold + ?Sized>(
984 _visitor: &mut V, _i: #ty
985 ) -> #ty {
986 #fold_impl
987 }
988 });
989 }
Nika Layzell27726662017-10-24 23:16:35 -0400990 }
991}
992
David Tolnay6af48992018-08-01 11:16:28 -0700993fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700994 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -0700995 write!(
996 file,
997 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
998 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -0700999 let mut config = rustfmt::Config::default();
1000 config.set().emit_mode(rustfmt::EmitMode::Stdout);
1001 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -07001002 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001003 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001004 session
1005 .format(rustfmt::Input::Text(content.to_string()))
1006 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001007}
1008
Nika Layzell27726662017-10-24 23:16:35 -04001009fn main() {
1010 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001011 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001012
Nika Layzellefb83ba2017-12-19 18:23:55 -05001013 // Load in any terminal types
1014 for &tt in TERMINAL_TYPES {
1015 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001016 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001017 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001018 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001019 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001020 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001021 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001022 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001023 }),
1024 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001025 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001026 data: Data::Struct(DataStruct {
1027 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001028 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001029 semi_token: None,
1030 }),
1031 },
hcplaa511792018-05-29 07:13:01 +03001032 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001033 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001034 },
David Tolnayd67fb752017-12-27 13:50:29 -05001035 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001036 }
1037
David Tolnay6af48992018-08-01 11:16:28 -07001038 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001039 for s in lookup.values() {
1040 codegen::generate(&mut state, &lookup, s);
1041 }
1042
David Tolnay6af48992018-08-01 11:16:28 -07001043 let full_macro = quote! {
1044 #[cfg(feature = "full")]
1045 macro_rules! full {
1046 ($e:expr) => {
1047 $e
1048 };
1049 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001050
David Tolnay6af48992018-08-01 11:16:28 -07001051 #[cfg(all(feature = "derive", not(feature = "full")))]
1052 macro_rules! full {
1053 ($e:expr) => {
1054 unreachable!()
1055 };
1056 }
1057 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001058
David Tolnay6af48992018-08-01 11:16:28 -07001059 let skip_macro = quote! {
1060 #[cfg(any(feature = "full", feature = "derive"))]
1061 macro_rules! skip {
1062 ($($tt:tt)*) => {};
1063 }
1064 };
1065
1066 let fold_trait = state.fold_trait;
1067 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001068 write_file(
1069 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001070 quote! {
1071 // Unreachable code is generated sometimes without the full feature.
1072 #![allow(unreachable_code)]
Nika Layzell27726662017-10-24 23:16:35 -04001073
David Tolnay6af48992018-08-01 11:16:28 -07001074 use *;
1075 #[cfg(any(feature = "full", feature = "derive"))]
1076 use token::{Brace, Bracket, Paren, Group};
1077 use proc_macro2::Span;
1078 #[cfg(any(feature = "full", feature = "derive"))]
1079 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001080
David Tolnay6af48992018-08-01 11:16:28 -07001081 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001082
David Tolnay6af48992018-08-01 11:16:28 -07001083 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1084 ///
1085 /// See the [module documentation] for details.
1086 ///
1087 /// [module documentation]: index.html
1088 ///
1089 /// *This trait is available if Syn is built with the `"fold"` feature.*
1090 pub trait Fold {
1091 #fold_trait
1092 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001093
David Tolnay6af48992018-08-01 11:16:28 -07001094 #[cfg(any(feature = "full", feature = "derive"))]
1095 macro_rules! fold_span_only {
1096 ($f:ident : $t:ident) => {
1097 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1098 let span = _visitor.fold_span(_i.span());
1099 _i.set_span(span);
1100 _i
1101 }
1102 }
1103 }
Nika Layzell27726662017-10-24 23:16:35 -04001104
David Tolnay6af48992018-08-01 11:16:28 -07001105 #[cfg(any(feature = "full", feature = "derive"))]
1106 fold_span_only!(fold_lit_byte: LitByte);
1107 #[cfg(any(feature = "full", feature = "derive"))]
1108 fold_span_only!(fold_lit_byte_str: LitByteStr);
1109 #[cfg(any(feature = "full", feature = "derive"))]
1110 fold_span_only!(fold_lit_char: LitChar);
1111 #[cfg(any(feature = "full", feature = "derive"))]
1112 fold_span_only!(fold_lit_float: LitFloat);
1113 #[cfg(any(feature = "full", feature = "derive"))]
1114 fold_span_only!(fold_lit_int: LitInt);
1115 #[cfg(any(feature = "full", feature = "derive"))]
1116 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001117
David Tolnay6af48992018-08-01 11:16:28 -07001118 #fold_impl
1119 },
David Tolnayae0009e2018-08-01 00:40:00 -07001120 );
Nika Layzell27726662017-10-24 23:16:35 -04001121
David Tolnay6af48992018-08-01 11:16:28 -07001122 let visit_trait = state.visit_trait;
1123 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001124 write_file(
1125 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001126 quote! {
David Tolnayc1f55792018-11-21 01:39:42 -08001127 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
Nika Layzell27726662017-10-24 23:16:35 -04001128
David Tolnay6af48992018-08-01 11:16:28 -07001129 use *;
1130 #[cfg(any(feature = "full", feature = "derive"))]
1131 use punctuated::Punctuated;
1132 use proc_macro2::Span;
1133 #[cfg(any(feature = "full", feature = "derive"))]
1134 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001135
David Tolnay6af48992018-08-01 11:16:28 -07001136 #full_macro
1137 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001138
David Tolnay6af48992018-08-01 11:16:28 -07001139 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1140 ///
1141 /// See the [module documentation] for details.
1142 ///
1143 /// [module documentation]: index.html
1144 ///
1145 /// *This trait is available if Syn is built with the `"visit"` feature.*
1146 pub trait Visit<'ast> {
1147 #visit_trait
1148 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001149
David Tolnay6af48992018-08-01 11:16:28 -07001150 #visit_impl
1151 },
David Tolnayae0009e2018-08-01 00:40:00 -07001152 );
Nika Layzell27726662017-10-24 23:16:35 -04001153
David Tolnay6af48992018-08-01 11:16:28 -07001154 let visit_mut_trait = state.visit_mut_trait;
1155 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001156 write_file(
1157 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001158 quote! {
David Tolnay6af48992018-08-01 11:16:28 -07001159 use *;
1160 #[cfg(any(feature = "full", feature = "derive"))]
1161 use punctuated::Punctuated;
1162 use proc_macro2::Span;
1163 #[cfg(any(feature = "full", feature = "derive"))]
1164 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001165
David Tolnay6af48992018-08-01 11:16:28 -07001166 #full_macro
1167 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001168
David Tolnay6af48992018-08-01 11:16:28 -07001169 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1170 /// place.
1171 ///
1172 /// See the [module documentation] for details.
1173 ///
1174 /// [module documentation]: index.html
1175 ///
1176 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1177 pub trait VisitMut {
1178 #visit_mut_trait
1179 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001180
David Tolnay6af48992018-08-01 11:16:28 -07001181 #visit_mut_impl
1182 },
David Tolnayae0009e2018-08-01 00:40:00 -07001183 );
Nika Layzell27726662017-10-24 23:16:35 -04001184}