blob: 25d72ca22ad5f363fb0cc00a72befbc8fe09fbf7 [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;
David Tolnay8c81f622018-07-31 23:34:35 -070022extern crate rustfmt_nightly as rustfmt;
David Tolnay5f794802018-11-24 14:51:21 -080023extern crate syn;
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 Tolnay5f794802018-11-24 14:51:21 -080049 a.leading_colon.is_none() && a.segments.len() == 1 && a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040050}
51
Alex Crichton715862b2018-05-17 12:31:49 -070052fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040053 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070054 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040055 attr.to_tokens(&mut features);
56 }
57 }
58 features
59}
60
61#[derive(Clone)]
62pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050063 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070064 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040065 // True if this is an ast_enum_of_structs! item with a #full annotation.
66 eos_full: bool,
67}
68
David Tolnayf0d63bf2017-12-26 12:29:47 -050069impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040070 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050072 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040073 .field("features", &self.features.to_string())
74 .finish()
75 }
76}
77
78// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050079type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040080
David Tolnay01ed0ce2018-05-20 20:18:14 -070081fn load_file<P: AsRef<Path>>(
82 name: P,
83 features: &TokenStream,
84 lookup: &mut Lookup,
85) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040086 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050087 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040088
89 let mut f = File::open(name)?;
90 let mut src = String::new();
91 f.read_to_string(&mut src)?;
92
93 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050094 let file =
95 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -040096
97 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -050098 'items: for item in file.items {
99 match item {
100 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400101 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800102 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400103 continue;
104 }
105
106 // We don't want to try to load the generated rust files and
107 // parse them, so we ignore them here.
108 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700109 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400110 continue 'items;
111 }
112 }
113
114 // Lookup any #[cfg()] attributes on the module and add them to
115 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800116 //
117 // The derive module is weird because it is built with either
118 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700119 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800120 quote!(#[cfg(feature = "derive")])
121 } else {
122 get_features(&item.attrs, features.clone())
123 };
Nika Layzell27726662017-10-24 23:16:35 -0400124
125 // Look up the submodule file, and recursively parse it.
126 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700127 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500128 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400129 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500130 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400131 // Lookip any #[cfg()] attributes directly on the macro
132 // invocation, and add them to the feature set.
133 let features = get_features(&item.attrs, features.clone());
134
135 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800136 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700137 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700138 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500139 .map_err(|_| err_msg("failed to parse ast_struct"))?
140 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700141 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700142 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500143 .map_err(|_| err_msg("failed to parse ast_enum"))?
144 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700145 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700146 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500147 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500148 .0
Nika Layzell27726662017-10-24 23:16:35 -0400149 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500150 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400151 };
152
153 // Record our features on the parsed AstItems.
154 for mut item in found {
155 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700156 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400157 }
158 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500159 Item::Struct(item) => {
160 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700161 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700162 lookup.insert(
163 ident.clone(),
164 AstItem {
165 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700166 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700167 vis: item.vis,
168 attrs: item.attrs,
169 generics: item.generics,
170 data: Data::Struct(DataStruct {
171 fields: item.fields,
172 struct_token: item.struct_token,
173 semi_token: item.semi_token,
174 }),
175 },
176 features: features.clone(),
177 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500178 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700179 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500180 }
181 }
Nika Layzell27726662017-10-24 23:16:35 -0400182 _ => {}
183 }
184 }
185 Ok(())
186}
187
188mod parsing {
189 use super::AstItem;
190
David Tolnay01ed0ce2018-05-20 20:18:14 -0700191 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500192 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700193 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400194 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400195
David Tolnay64f03842018-08-30 21:34:40 -0700196 fn peek_tag(input: ParseStream, tag: &str) -> bool {
197 let ahead = input.fork();
David Tolnay5f794802018-11-24 14:51:21 -0800198 ahead.parse::<Token![#]>().is_ok()
199 && ahead
200 .parse::<Ident>()
201 .map(|ident| ident == tag)
202 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700203 }
204
Nika Layzell27726662017-10-24 23:16:35 -0400205 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
206 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700207 fn full(input: ParseStream) -> (TokenStream, bool) {
208 if peek_tag(input, "full") {
209 input.parse::<Token![#]>().unwrap();
210 input.parse::<Ident>().unwrap();
211 (quote!(#[cfg(feature = "full")]), true)
212 } else {
213 (quote!(), false)
214 }
215 }
Nika Layzell27726662017-10-24 23:16:35 -0400216
David Tolnay64f03842018-08-30 21:34:40 -0700217 fn skip_manual_extra_traits(input: ParseStream) {
218 if peek_tag(input, "manual_extra_traits") {
219 input.parse::<Token![#]>().unwrap();
220 input.parse::<Ident>().unwrap();
221 }
222 }
David Tolnay28c5a462017-12-27 01:59:30 -0500223
Nika Layzell27726662017-10-24 23:16:35 -0400224 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700225 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
226 let ident: Ident = input.parse()?;
227 let (features, eos_full) = full(input);
228 skip_manual_extra_traits(input);
229 let rest: TokenStream = input.parse()?;
230 Ok(AstItem {
231 ast: syn::parse2(quote! {
232 pub struct #ident #rest
233 })?,
David Tolnay9a518142018-11-21 01:38:50 -0800234 features,
235 eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400236 })
David Tolnay64f03842018-08-30 21:34:40 -0700237 }
Nika Layzell27726662017-10-24 23:16:35 -0400238
239 // ast_struct! parsing
240 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700241 impl Parse for AstStruct {
242 fn parse(input: ParseStream) -> Result<Self> {
243 input.call(Attribute::parse_outer)?;
244 input.parse::<Token![pub]>()?;
245 input.parse::<Token![struct]>()?;
246 let res = input.call(ast_struct_inner)?;
247 Ok(AstStruct(vec![res]))
248 }
Nika Layzell27726662017-10-24 23:16:35 -0400249 }
250
David Tolnay64f03842018-08-30 21:34:40 -0700251 fn no_visit(input: ParseStream) -> bool {
252 if peek_tag(input, "no_visit") {
253 input.parse::<Token![#]>().unwrap();
254 input.parse::<Ident>().unwrap();
255 true
256 } else {
257 false
258 }
259 }
David Tolnay360efd22018-01-04 23:35:26 -0800260
Nika Layzell27726662017-10-24 23:16:35 -0400261 // ast_enum! parsing
262 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700263 impl Parse for AstEnum {
264 fn parse(input: ParseStream) -> Result<Self> {
265 input.call(Attribute::parse_outer)?;
266 input.parse::<Token![pub]>()?;
267 input.parse::<Token![enum]>()?;
268 let ident: Ident = input.parse()?;
269 let no_visit = no_visit(input);
270 let rest: TokenStream = input.parse()?;
271 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800272 vec![]
273 } else {
274 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700275 ast: syn::parse2(quote! {
276 pub enum #ident #rest
277 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800278 features: quote!(),
279 eos_full: false,
280 }]
281 }))
David Tolnay64f03842018-08-30 21:34:40 -0700282 }
Nika Layzell27726662017-10-24 23:16:35 -0400283 }
284
285 // A single variant of an ast_enum_of_structs!
286 struct EosVariant {
287 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500288 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400289 inner: Option<AstItem>,
290 }
David Tolnay64f03842018-08-30 21:34:40 -0700291 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
292 input.call(Attribute::parse_outer)?;
293 input.parse::<Token![pub]>()?;
294 let variant: Ident = input.parse()?;
295 let (member, inner) = if input.peek(token::Paren) {
296 let content;
297 parenthesized!(content in input);
298 if content.fork().call(ast_struct_inner).is_ok() {
299 let item = content.call(ast_struct_inner)?;
300 (Some(Path::from(item.ast.ident.clone())), Some(item))
301 } else {
302 let path: Path = content.parse()?;
303 (Some(path), None)
304 }
305 } else {
306 (None, None)
307 };
308 input.parse::<Token![,]>()?;
309 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400310 name: variant,
David Tolnay9a518142018-11-21 01:38:50 -0800311 member,
312 inner,
Nika Layzell27726662017-10-24 23:16:35 -0400313 })
David Tolnay64f03842018-08-30 21:34:40 -0700314 }
Nika Layzell27726662017-10-24 23:16:35 -0400315
316 // ast_enum_of_structs! parsing
317 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700318 impl Parse for AstEnumOfStructs {
319 fn parse(input: ParseStream) -> Result<Self> {
320 input.call(Attribute::parse_outer)?;
321 input.parse::<Token![pub]>()?;
322 input.parse::<Token![enum]>()?;
323 let ident: Ident = input.parse()?;
324
325 let content;
326 braced!(content in input);
327 let mut variants = Vec::new();
328 while !content.is_empty() {
329 variants.push(content.call(eos_variant)?);
330 }
331
332 if let Some(ident) = input.parse::<Option<Ident>>()? {
333 assert_eq!(ident, "do_not_generate_to_tokens");
334 }
335
336 let enum_item = {
337 let variants = variants.iter().map(|v| {
338 let name = v.name.clone();
339 match v.member {
340 Some(ref member) => quote!(#name(#member)),
341 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700342 }
David Tolnay64f03842018-08-30 21:34:40 -0700343 });
344 parse_quote! {
345 pub enum #ident {
346 #(#variants),*
347 }
348 }
349 };
350 let mut items = vec![AstItem {
351 ast: enum_item,
352 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700353 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700354 }];
355 items.extend(variants.into_iter().filter_map(|v| v.inner));
356 Ok(AstEnumOfStructs(items))
357 }
Nika Layzell27726662017-10-24 23:16:35 -0400358 }
359}
360
361mod codegen {
362 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700363 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700364 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700365 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700366 use syn::ext::IdentExt;
367 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700368 use syn::punctuated::Punctuated;
369 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400370
371 #[derive(Default)]
372 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700373 pub visit_trait: TokenStream,
374 pub visit_impl: TokenStream,
375 pub visit_mut_trait: TokenStream,
376 pub visit_mut_impl: TokenStream,
377 pub fold_trait: TokenStream,
378 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400379 }
380
David Tolnay4a918742017-12-28 16:54:41 -0500381 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700382 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400383 }
384
David Tolnay3d772182017-12-28 17:18:53 -0500385 enum RelevantType<'a> {
386 Box(&'a Type),
387 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500388 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500389 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500390 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500391 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400392 TokenPunct(TokenStream),
393 TokenKeyword(TokenStream),
394 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500395 Pass,
396 }
Nika Layzell27726662017-10-24 23:16:35 -0400397
David Tolnay3d772182017-12-28 17:18:53 -0500398 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
399 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700400 Type::Path(TypePath {
401 qself: None,
402 ref path,
403 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800404 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700405 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500406 "Box" => RelevantType::Box(first_arg(&last.arguments)),
407 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500408 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500409 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500410 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400411 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500412 }
David Tolnay3d772182017-12-28 17:18:53 -0500413 _ => {
414 if let Some(item) = lookup.get(&last.ident) {
415 RelevantType::Simple(item)
416 } else {
417 RelevantType::Pass
418 }
419 }
420 }
Nika Layzell27726662017-10-24 23:16:35 -0400421 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700422 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
423 Type::Macro(TypeMacro { ref mac })
424 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
425 {
David Tolnay86cb9452018-08-31 10:35:09 -0700426 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400427 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
428 if is_ident && !is_underscore {
429 RelevantType::TokenKeyword(mac.into_token_stream())
430 } else {
431 RelevantType::TokenPunct(mac.into_token_stream())
432 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500433 }
David Tolnay3d772182017-12-28 17:18:53 -0500434 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400435 }
436 }
437
438 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
439 enum Kind {
440 Visit,
441 VisitMut,
442 Fold,
443 }
444
David Tolnayf0d63bf2017-12-26 12:29:47 -0500445 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700446 Borrowed(TokenStream),
447 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500448 }
449
David Tolnay83db9272017-12-28 17:02:31 -0500450 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700451 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500452
David Tolnayf0d63bf2017-12-26 12:29:47 -0500453 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700454 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500455 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500456 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500457 }
458 }
459
Alex Crichton715862b2018-05-17 12:31:49 -0700460 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500461 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500462 Borrowed(ref n) => n.clone(),
463 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500464 }
465 }
466
Alex Crichton715862b2018-05-17 12:31:49 -0700467 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500468 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500469 Borrowed(ref n) => n.clone(),
470 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500471 }
472 }
473
Alex Crichton715862b2018-05-17 12:31:49 -0700474 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500475 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500476 Borrowed(ref n) => quote!(*#n),
477 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500478 }
479 }
480 }
481
Nika Layzellc08227a2017-12-04 16:30:17 -0500482 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400483 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500484 PathArguments::AngleBracketed(ref data) => data,
485 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400486 };
487
David Tolnay01ed0ce2018-05-20 20:18:14 -0700488 match **data
489 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500490 .first()
491 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800492 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500493 {
David Tolnayea9ae892017-12-26 01:44:32 -0500494 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500495 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500496 }
Nika Layzell27726662017-10-24 23:16:35 -0400497 }
498
David Tolnay6af48992018-08-01 11:16:28 -0700499 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
500 let ident = under_name(item.ast.ident.clone());
501
David Tolnay4a918742017-12-28 16:54:41 -0500502 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700503 Visit => {
504 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
505 let name = name.ref_tokens();
506 quote! {
507 _visitor.#method(#name)
508 }
509 }
510 VisitMut => {
511 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
512 let name = name.ref_mut_tokens();
513 quote! {
514 _visitor.#method(#name)
515 }
516 }
517 Fold => {
518 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
519 let name = name.owned_tokens();
520 quote! {
521 _visitor.#method(#name)
522 }
523 }
Nika Layzell27726662017-10-24 23:16:35 -0400524 }
525 }
526
David Tolnay6af48992018-08-01 11:16:28 -0700527 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500528 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500529 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500530 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700531 Fold => quote! {
532 Box::new(#res)
533 },
David Tolnay83db9272017-12-28 17:02:31 -0500534 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500535 })
Nika Layzell27726662017-10-24 23:16:35 -0400536 }
537
David Tolnay6af48992018-08-01 11:16:28 -0700538 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500539 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500540 Visit | VisitMut => Borrowed(quote!(it)),
541 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500542 };
David Tolnay39d0a202017-12-28 18:19:00 -0500543 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500544 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700545 Visit => {
546 let name = name.ref_tokens();
547 quote! {
548 for it in #name {
549 #val
550 }
551 }
552 }
553 VisitMut => {
554 let name = name.ref_mut_tokens();
555 quote! {
556 for it in #name {
557 #val
558 }
559 }
560 }
561 Fold => {
562 let name = name.owned_tokens();
563 quote! {
564 FoldHelper::lift(#name, |it| { #val })
565 }
566 }
David Tolnay3d772182017-12-28 17:18:53 -0500567 })
568 }
569
David Tolnay6eff4da2018-01-01 20:27:45 -0800570 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500571 elem: &Type,
572 lookup: &Lookup,
573 kind: Kind,
574 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700575 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500576 let operand = match kind {
577 Visit | VisitMut => Borrowed(quote!(it)),
578 Fold => Owned(quote!(it)),
579 };
David Tolnay39d0a202017-12-28 18:19:00 -0500580 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500581 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700582 Visit => {
583 let name = name.ref_tokens();
584 quote! {
585 for el in Punctuated::pairs(#name) {
586 let it = el.value();
587 #val
588 }
589 }
590 }
591 VisitMut => {
592 let name = name.ref_mut_tokens();
593 quote! {
594 for mut el in Punctuated::pairs_mut(#name) {
595 let it = el.value_mut();
596 #val
597 }
598 }
599 }
600 Fold => {
601 let name = name.owned_tokens();
602 quote! {
603 FoldHelper::lift(#name, |it| { #val })
604 }
605 }
David Tolnay4a918742017-12-28 16:54:41 -0500606 })
Nika Layzell27726662017-10-24 23:16:35 -0400607 }
608
David Tolnay6af48992018-08-01 11:16:28 -0700609 fn option_visit(
610 elem: &Type,
611 lookup: &Lookup,
612 kind: Kind,
613 name: &Operand,
614 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500615 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500616 Visit | VisitMut => Borrowed(quote!(it)),
617 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500618 };
David Tolnay39d0a202017-12-28 18:19:00 -0500619 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700620 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500621 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700622 Visit => quote! {
623 if let Some(ref it) = #name {
624 #val
625 }
626 },
627 VisitMut => quote! {
628 if let Some(ref mut it) = #name {
629 #val
630 }
631 },
632 Fold => quote! {
633 (#name).map(|it| { #val })
634 },
David Tolnay4a918742017-12-28 16:54:41 -0500635 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400636 }
637
David Tolnay5c4c0b52017-12-28 17:58:54 -0500638 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500639 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500640 lookup: &Lookup,
641 kind: Kind,
642 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700643 ) -> Option<TokenStream> {
644 if elems.is_empty() {
645 return None;
646 }
647
648 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500649 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500650 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500651 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500652 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700653 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700654 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500655 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700656 Fold => code.append_all(quote!(,)),
657 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500658 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500659 }
David Tolnay6af48992018-08-01 11:16:28 -0700660 Some(match kind {
661 Fold => quote! {
662 (#code)
663 },
664 Visit | VisitMut => code,
665 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500666 }
667
David Tolnay7ac699c2018-08-24 14:00:58 -0400668 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700669 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500670 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700671 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400672 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700673 },
674 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400675 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700676 },
677 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400678 tokens_helper(_visitor, &mut #name.spans)
679 },
680 }
681 }
682
683 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
684 let name = name.tokens();
685 match kind {
686 Fold => quote! {
687 #ty(tokens_helper(_visitor, &#name.span))
688 },
689 Visit => quote! {
690 tokens_helper(_visitor, &#name.span)
691 },
692 VisitMut => quote! {
693 tokens_helper(_visitor, &mut #name.span)
694 },
695 }
696 }
697
698 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
699 let name = name.tokens();
700 match kind {
701 Fold => quote! {
702 #ty(tokens_helper(_visitor, &#name.span))
703 },
704 Visit => quote! {
705 tokens_helper(_visitor, &#name.span)
706 },
707 VisitMut => quote! {
708 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700709 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500710 }
711 }
712
David Tolnay6af48992018-08-01 11:16:28 -0700713 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500714 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700715 Fold => name.owned_tokens(),
716 Visit | VisitMut => {
717 let name = name.tokens();
718 quote! {
719 skip!(#name)
720 }
721 }
David Tolnay4a918742017-12-28 16:54:41 -0500722 }
723 }
724
David Tolnay6af48992018-08-01 11:16:28 -0700725 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500726 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700727 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
728 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
729 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
730 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
731 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500732 RelevantType::Simple(item) => {
733 let mut res = simple_visit(item, kind, name);
734 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700735 quote! {
736 full!(#res)
737 }
David Tolnay3d772182017-12-28 17:18:53 -0500738 } else {
739 res
740 })
741 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400742 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
743 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
744 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700745 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400746 }
Nika Layzell27726662017-10-24 23:16:35 -0400747 }
748
749 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700750 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700751 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700752 let ty = &s.ast.ident;
753 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
754 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
755 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400756
David Tolnay6af48992018-08-01 11:16:28 -0700757 let mut visit_impl = TokenStream::new();
758 let mut visit_mut_impl = TokenStream::new();
759 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400760
David Tolnaye3d41b72017-12-31 15:24:00 -0500761 match s.ast.data {
762 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700763 let mut visit_variants = TokenStream::new();
764 let mut visit_mut_variants = TokenStream::new();
765 let mut fold_variants = TokenStream::new();
766
Nika Layzell27726662017-10-24 23:16:35 -0400767 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700768 let variant_ident = &variant.ident;
769
770 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500771 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
772 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700773 let mut bind_visit_fields = TokenStream::new();
774 let mut bind_visit_mut_fields = TokenStream::new();
775 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400776
David Tolnay6af48992018-08-01 11:16:28 -0700777 let mut visit_fields = TokenStream::new();
778 let mut visit_mut_fields = TokenStream::new();
779 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400780
David Tolnay6af48992018-08-01 11:16:28 -0700781 for (idx, field) in fields.unnamed.iter().enumerate() {
782 let name = format!("_binding_{}", idx);
783 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400784
David Tolnay6af48992018-08-01 11:16:28 -0700785 bind_visit_fields.append_all(quote! {
786 ref #binding,
787 });
788 bind_visit_mut_fields.append_all(quote! {
789 ref mut #binding,
790 });
791 bind_fold_fields.append_all(quote! {
792 #binding,
793 });
Nika Layzell27726662017-10-24 23:16:35 -0400794
David Tolnay6af48992018-08-01 11:16:28 -0700795 let borrowed_binding = Borrowed(quote!(#binding));
796 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400797
David Tolnay6af48992018-08-01 11:16:28 -0700798 visit_fields.append_all(
799 visit(&field.ty, lookup, Visit, &borrowed_binding)
800 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
801 );
802 visit_mut_fields.append_all(
803 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
804 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
805 );
806 fold_fields.append_all(
807 visit(&field.ty, lookup, Fold, &owned_binding)
808 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
809 );
Nika Layzell27726662017-10-24 23:16:35 -0400810
David Tolnay6af48992018-08-01 11:16:28 -0700811 visit_fields.append_all(quote!(;));
812 visit_mut_fields.append_all(quote!(;));
813 fold_fields.append_all(quote!(,));
814 }
Nika Layzell27726662017-10-24 23:16:35 -0400815
David Tolnay6af48992018-08-01 11:16:28 -0700816 visit_variants.append_all(quote! {
817 #ty::#variant_ident(#bind_visit_fields) => {
818 #visit_fields
819 }
820 });
821
822 visit_mut_variants.append_all(quote! {
823 #ty::#variant_ident(#bind_visit_mut_fields) => {
824 #visit_mut_fields
825 }
826 });
827
828 fold_variants.append_all(quote! {
829 #ty::#variant_ident(#bind_fold_fields) => {
830 #ty::#variant_ident(
831 #fold_fields
832 )
833 }
834 });
Nika Layzell27726662017-10-24 23:16:35 -0400835 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500836 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700837 visit_variants.append_all(quote! {
838 #ty::#variant_ident => {}
839 });
840 visit_mut_variants.append_all(quote! {
841 #ty::#variant_ident => {}
842 });
843 fold_variants.append_all(quote! {
844 #ty::#variant_ident => {
845 #ty::#variant_ident
846 }
847 });
Nika Layzell27726662017-10-24 23:16:35 -0400848 }
Nika Layzell27726662017-10-24 23:16:35 -0400849 }
Nika Layzell27726662017-10-24 23:16:35 -0400850 }
David Tolnay6af48992018-08-01 11:16:28 -0700851
852 visit_impl.append_all(quote! {
853 match *_i {
854 #visit_variants
855 }
856 });
857
858 visit_mut_impl.append_all(quote! {
859 match *_i {
860 #visit_mut_variants
861 }
862 });
863
864 fold_impl.append_all(quote! {
865 match _i {
866 #fold_variants
867 }
868 });
Nika Layzell27726662017-10-24 23:16:35 -0400869 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500870 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700871 let mut fold_fields = TokenStream::new();
872
David Tolnaybc5b2c02018-08-02 00:18:23 -0700873 for (idx, field) in v.fields.iter().enumerate() {
874 let id = match field.ident {
875 Some(ref ident) => Member::Named(ident.clone()),
876 None => Member::Unnamed(Index::from(idx)),
877 };
878 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700879 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
880 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
881 visit_impl.append_all(quote! {
882 #visit_field;
883 });
884 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
885 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
886 visit_mut_impl.append_all(quote! {
887 #visit_mut_field;
888 });
David Tolnay83db9272017-12-28 17:02:31 -0500889 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700890 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400891 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700892 fold_fields.append_all(quote! {
893 #name: #fold,
894 });
Nika Layzell27726662017-10-24 23:16:35 -0400895 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700896 fold_fields.append_all(quote! {
897 #fold,
898 });
Nika Layzell27726662017-10-24 23:16:35 -0400899 }
900 }
901
David Tolnaye3d41b72017-12-31 15:24:00 -0500902 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700903 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
904 #ty {
905 #fold_fields
906 }
907 }),
908 Fields::Unit => {
909 if ty == "Ident" {
910 fold_impl.append_all(quote! {
911 let mut _i = _i;
912 let span = _visitor.fold_span(_i.span());
913 _i.set_span(span);
914 });
915 }
916 fold_impl.append_all(quote! {
917 _i
918 });
919 }
Nika Layzell27726662017-10-24 23:16:35 -0400920 };
921 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500922 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400923 }
924
David Tolnay6af48992018-08-01 11:16:28 -0700925 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800926 if let Data::Struct(ref data) = s.ast.data {
927 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700928 if fields
929 .named
930 .iter()
931 .any(|field| field.vis == Visibility::Inherited)
932 {
David Tolnay360efd22018-01-04 23:35:26 -0800933 // Discard the generated impl if there are private fields.
934 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700935 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800936 }
937 }
David Tolnayd0adf522017-12-29 01:30:07 -0500938 }
David Tolnay6af48992018-08-01 11:16:28 -0700939
940 state.visit_trait.append_all(quote! {
941 #features
942 fn #visit_fn(&mut self, i: &'ast #ty) {
943 #visit_fn(self, i)
944 }
945 });
946
947 state.visit_impl.append_all(quote! {
948 #features
949 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
950 _visitor: &mut V, _i: &'ast #ty
951 ) {
952 #visit_impl
953 }
954 });
955
956 state.visit_mut_trait.append_all(quote! {
957 #features
958 fn #visit_mut_fn(&mut self, i: &mut #ty) {
959 #visit_mut_fn(self, i)
960 }
961 });
962
963 state.visit_mut_impl.append_all(quote! {
964 #features
965 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
966 _visitor: &mut V, _i: &mut #ty
967 ) {
968 #visit_mut_impl
969 }
970 });
971
972 state.fold_trait.append_all(quote! {
973 #features
974 fn #fold_fn(&mut self, i: #ty) -> #ty {
975 #fold_fn(self, i)
976 }
977 });
978
979 if include_fold_impl {
980 state.fold_impl.append_all(quote! {
981 #features
982 pub fn #fold_fn<V: Fold + ?Sized>(
983 _visitor: &mut V, _i: #ty
984 ) -> #ty {
985 #fold_impl
986 }
987 });
988 }
Nika Layzell27726662017-10-24 23:16:35 -0400989 }
990}
991
David Tolnay6af48992018-08-01 11:16:28 -0700992fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700993 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -0700994 write!(
995 file,
996 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
David Tolnay5f794802018-11-24 14:51:21 -0800997 )
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}