blob: daa0cc0785bffa0f1b6f5dbbb284c31f84803155 [file] [log] [blame]
David Tolnay4b4c4b62018-01-06 13:48:05 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
Nika Layzell27726662017-10-24 23:16:35 -04003//! discovers structs and enums declared with the `ast_*` macros and generates
4//! the functions for those types.
5//!
6//! It makes a few assumptions about the target crate:
7//! 1. All structs which are discovered must be re-exported in the root of the
8//! crate, even if they were declared in a submodule.
9//! 2. This code cannot discover submodules which are located in subdirectories
10//! - only submodules located in the same directory.
11//! 3. The path to `syn` is hardcoded.
12
David Tolnay6af48992018-08-01 11:16:28 -070013#![recursion_limit = "128"]
David Tolnay6b46a702018-08-01 23:51:32 -070014#![cfg_attr(
15 feature = "cargo-clippy",
16 allow(
17 needless_pass_by_value,
18 redundant_closure,
19 write_with_newline,
20 )
21)]
David Tolnayea9ae892017-12-26 01:44:32 -050022
David Tolnayd67fb752017-12-27 13:50:29 -050023#[macro_use]
24extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040025extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050026extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050027#[macro_use]
28extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050029#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050030extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070031extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040032
David Tolnayd67fb752017-12-27 13:50:29 -050033use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070034use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070035use quote::ToTokens;
36use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040037
David Tolnay01ed0ce2018-05-20 20:18:14 -070038use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050039use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040040use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070041use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040042use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040043
44const SYN_CRATE_ROOT: &str = "../src/lib.rs";
45
46const FOLD_SRC: &str = "../src/gen/fold.rs";
47const VISIT_SRC: &str = "../src/gen/visit.rs";
48const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
49
David Tolnayd67fb752017-12-27 13:50:29 -050050const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040051
Alex Crichton131308c2018-05-18 14:00:24 -070052const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050053
Alex Crichtond261d092018-05-18 13:47:35 -070054const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050055
Alex Crichtona74a1c82018-05-16 10:20:44 -070056fn path_eq(a: &syn::Path, b: &str) -> bool {
57 if a.global() {
David Tolnay01ed0ce2018-05-20 20:18:14 -070058 return false;
Nika Layzell27726662017-10-24 23:16:35 -040059 }
Alex Crichtona74a1c82018-05-16 10:20:44 -070060 if a.segments.len() != 1 {
David Tolnay01ed0ce2018-05-20 20:18:14 -070061 return false;
Alex Crichtona74a1c82018-05-16 10:20:44 -070062 }
David Tolnay446f7d62018-05-20 17:58:15 -070063 a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040064}
65
Alex Crichton715862b2018-05-17 12:31:49 -070066fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040067 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070068 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040069 attr.to_tokens(&mut features);
70 }
71 }
72 features
73}
74
75#[derive(Clone)]
76pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050077 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070078 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040079 // True if this is an ast_enum_of_structs! item with a #full annotation.
80 eos_full: bool,
81}
82
David Tolnayf0d63bf2017-12-26 12:29:47 -050083impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040084 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050086 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040087 .field("features", &self.features.to_string())
88 .finish()
89 }
90}
91
92// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050093type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040094
David Tolnay01ed0ce2018-05-20 20:18:14 -070095fn load_file<P: AsRef<Path>>(
96 name: P,
97 features: &TokenStream,
98 lookup: &mut Lookup,
99) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -0400100 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -0500101 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -0400102
103 let mut f = File::open(name)?;
104 let mut src = String::new();
105 f.read_to_string(&mut src)?;
106
107 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500108 let file =
109 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400110
111 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500112 'items: for item in file.items {
113 match item {
114 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400115 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800116 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400117 continue;
118 }
119
120 // We don't want to try to load the generated rust files and
121 // parse them, so we ignore them here.
122 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700123 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400124 continue 'items;
125 }
126 }
127
128 // Lookup any #[cfg()] attributes on the module and add them to
129 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800130 //
131 // The derive module is weird because it is built with either
132 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700133 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800134 quote!(#[cfg(feature = "derive")])
135 } else {
136 get_features(&item.attrs, features.clone())
137 };
Nika Layzell27726662017-10-24 23:16:35 -0400138
139 // Look up the submodule file, and recursively parse it.
140 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700141 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500142 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400143 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500144 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400145 // Lookip any #[cfg()] attributes directly on the macro
146 // invocation, and add them to the feature set.
147 let features = get_features(&item.attrs, features.clone());
148
149 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800150 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700151 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700152 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500153 .map_err(|_| err_msg("failed to parse ast_struct"))?
154 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700155 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700156 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500157 .map_err(|_| err_msg("failed to parse ast_enum"))?
158 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700159 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700160 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500161 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500162 .0
Nika Layzell27726662017-10-24 23:16:35 -0400163 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500164 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400165 };
166
167 // Record our features on the parsed AstItems.
168 for mut item in found {
169 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700170 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400171 }
172 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500173 Item::Struct(item) => {
174 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700175 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700176 lookup.insert(
177 ident.clone(),
178 AstItem {
179 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700180 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700181 vis: item.vis,
182 attrs: item.attrs,
183 generics: item.generics,
184 data: Data::Struct(DataStruct {
185 fields: item.fields,
186 struct_token: item.struct_token,
187 semi_token: item.semi_token,
188 }),
189 },
190 features: features.clone(),
191 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500192 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700193 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500194 }
195 }
Nika Layzell27726662017-10-24 23:16:35 -0400196 _ => {}
197 }
198 }
199 Ok(())
200}
201
202mod parsing {
203 use super::AstItem;
204
David Tolnay01ed0ce2018-05-20 20:18:14 -0700205 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500206 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700207 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400208 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400209
David Tolnay64f03842018-08-30 21:34:40 -0700210 fn peek_tag(input: ParseStream, tag: &str) -> bool {
211 let ahead = input.fork();
212 ahead.parse::<Token![#]>().is_ok()
213 && ahead.parse::<Ident>().map(|ident| ident == tag).unwrap_or(false)
214 }
215
Nika Layzell27726662017-10-24 23:16:35 -0400216 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
217 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700218 fn full(input: ParseStream) -> (TokenStream, bool) {
219 if peek_tag(input, "full") {
220 input.parse::<Token![#]>().unwrap();
221 input.parse::<Ident>().unwrap();
222 (quote!(#[cfg(feature = "full")]), true)
223 } else {
224 (quote!(), false)
225 }
226 }
Nika Layzell27726662017-10-24 23:16:35 -0400227
David Tolnay64f03842018-08-30 21:34:40 -0700228 fn skip_manual_extra_traits(input: ParseStream) {
229 if peek_tag(input, "manual_extra_traits") {
230 input.parse::<Token![#]>().unwrap();
231 input.parse::<Ident>().unwrap();
232 }
233 }
David Tolnay28c5a462017-12-27 01:59:30 -0500234
Nika Layzell27726662017-10-24 23:16:35 -0400235 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700236 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
237 let ident: Ident = input.parse()?;
238 let (features, eos_full) = full(input);
239 skip_manual_extra_traits(input);
240 let rest: TokenStream = input.parse()?;
241 Ok(AstItem {
242 ast: syn::parse2(quote! {
243 pub struct #ident #rest
244 })?,
245 features: features,
246 eos_full: eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400247 })
David Tolnay64f03842018-08-30 21:34:40 -0700248 }
Nika Layzell27726662017-10-24 23:16:35 -0400249
250 // ast_struct! parsing
251 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700252 impl Parse for AstStruct {
253 fn parse(input: ParseStream) -> Result<Self> {
254 input.call(Attribute::parse_outer)?;
255 input.parse::<Token![pub]>()?;
256 input.parse::<Token![struct]>()?;
257 let res = input.call(ast_struct_inner)?;
258 Ok(AstStruct(vec![res]))
259 }
Nika Layzell27726662017-10-24 23:16:35 -0400260 }
261
David Tolnay64f03842018-08-30 21:34:40 -0700262 fn no_visit(input: ParseStream) -> bool {
263 if peek_tag(input, "no_visit") {
264 input.parse::<Token![#]>().unwrap();
265 input.parse::<Ident>().unwrap();
266 true
267 } else {
268 false
269 }
270 }
David Tolnay360efd22018-01-04 23:35:26 -0800271
Nika Layzell27726662017-10-24 23:16:35 -0400272 // ast_enum! parsing
273 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700274 impl Parse for AstEnum {
275 fn parse(input: ParseStream) -> Result<Self> {
276 input.call(Attribute::parse_outer)?;
277 input.parse::<Token![pub]>()?;
278 input.parse::<Token![enum]>()?;
279 let ident: Ident = input.parse()?;
280 let no_visit = no_visit(input);
281 let rest: TokenStream = input.parse()?;
282 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800283 vec![]
284 } else {
285 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700286 ast: syn::parse2(quote! {
287 pub enum #ident #rest
288 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800289 features: quote!(),
290 eos_full: false,
291 }]
292 }))
David Tolnay64f03842018-08-30 21:34:40 -0700293 }
Nika Layzell27726662017-10-24 23:16:35 -0400294 }
295
296 // A single variant of an ast_enum_of_structs!
297 struct EosVariant {
298 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500299 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400300 inner: Option<AstItem>,
301 }
David Tolnay64f03842018-08-30 21:34:40 -0700302 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
303 input.call(Attribute::parse_outer)?;
304 input.parse::<Token![pub]>()?;
305 let variant: Ident = input.parse()?;
306 let (member, inner) = if input.peek(token::Paren) {
307 let content;
308 parenthesized!(content in input);
309 if content.fork().call(ast_struct_inner).is_ok() {
310 let item = content.call(ast_struct_inner)?;
311 (Some(Path::from(item.ast.ident.clone())), Some(item))
312 } else {
313 let path: Path = content.parse()?;
314 (Some(path), None)
315 }
316 } else {
317 (None, None)
318 };
319 input.parse::<Token![,]>()?;
320 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400321 name: variant,
David Tolnay64f03842018-08-30 21:34:40 -0700322 member: member,
323 inner: inner,
Nika Layzell27726662017-10-24 23:16:35 -0400324 })
David Tolnay64f03842018-08-30 21:34:40 -0700325 }
Nika Layzell27726662017-10-24 23:16:35 -0400326
327 // ast_enum_of_structs! parsing
328 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700329 impl Parse for AstEnumOfStructs {
330 fn parse(input: ParseStream) -> Result<Self> {
331 input.call(Attribute::parse_outer)?;
332 input.parse::<Token![pub]>()?;
333 input.parse::<Token![enum]>()?;
334 let ident: Ident = input.parse()?;
335
336 let content;
337 braced!(content in input);
338 let mut variants = Vec::new();
339 while !content.is_empty() {
340 variants.push(content.call(eos_variant)?);
341 }
342
343 if let Some(ident) = input.parse::<Option<Ident>>()? {
344 assert_eq!(ident, "do_not_generate_to_tokens");
345 }
346
347 let enum_item = {
348 let variants = variants.iter().map(|v| {
349 let name = v.name.clone();
350 match v.member {
351 Some(ref member) => quote!(#name(#member)),
352 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700353 }
David Tolnay64f03842018-08-30 21:34:40 -0700354 });
355 parse_quote! {
356 pub enum #ident {
357 #(#variants),*
358 }
359 }
360 };
361 let mut items = vec![AstItem {
362 ast: enum_item,
363 features: quote!(),
364 eos_full: false,
365 }];
366 items.extend(variants.into_iter().filter_map(|v| v.inner));
367 Ok(AstEnumOfStructs(items))
368 }
Nika Layzell27726662017-10-24 23:16:35 -0400369 }
370}
371
372mod codegen {
373 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700374 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700375 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700376 use quote::{ToTokens, TokenStreamExt};
David Tolnay01ed0ce2018-05-20 20:18:14 -0700377 use syn::punctuated::Punctuated;
David Tolnay7ac699c2018-08-24 14:00:58 -0400378 use syn::synom::ext::IdentExt;
379 use syn::synom::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700380 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400381
382 #[derive(Default)]
383 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700384 pub visit_trait: TokenStream,
385 pub visit_impl: TokenStream,
386 pub visit_mut_trait: TokenStream,
387 pub visit_mut_impl: TokenStream,
388 pub fold_trait: TokenStream,
389 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400390 }
391
David Tolnay4a918742017-12-28 16:54:41 -0500392 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700393 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400394 }
395
David Tolnay3d772182017-12-28 17:18:53 -0500396 enum RelevantType<'a> {
397 Box(&'a Type),
398 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500399 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500400 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500401 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500402 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400403 TokenPunct(TokenStream),
404 TokenKeyword(TokenStream),
405 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500406 Pass,
407 }
Nika Layzell27726662017-10-24 23:16:35 -0400408
David Tolnay3d772182017-12-28 17:18:53 -0500409 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
410 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700411 Type::Path(TypePath {
412 qself: None,
413 ref path,
414 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800415 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700416 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500417 "Box" => RelevantType::Box(first_arg(&last.arguments)),
418 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500419 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500420 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500421 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400422 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500423 }
David Tolnay3d772182017-12-28 17:18:53 -0500424 _ => {
425 if let Some(item) = lookup.get(&last.ident) {
426 RelevantType::Simple(item)
427 } else {
428 RelevantType::Pass
429 }
430 }
431 }
Nika Layzell27726662017-10-24 23:16:35 -0400432 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700433 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
434 Type::Macro(TypeMacro { ref mac })
435 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
436 {
David Tolnay7ac699c2018-08-24 14:00:58 -0400437 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok() ;
438 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
439 if is_ident && !is_underscore {
440 RelevantType::TokenKeyword(mac.into_token_stream())
441 } else {
442 RelevantType::TokenPunct(mac.into_token_stream())
443 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500444 }
David Tolnay3d772182017-12-28 17:18:53 -0500445 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400446 }
447 }
448
449 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
450 enum Kind {
451 Visit,
452 VisitMut,
453 Fold,
454 }
455
David Tolnayf0d63bf2017-12-26 12:29:47 -0500456 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700457 Borrowed(TokenStream),
458 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500459 }
460
David Tolnay83db9272017-12-28 17:02:31 -0500461 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700462 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500463
David Tolnayf0d63bf2017-12-26 12:29:47 -0500464 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700465 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500466 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500467 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500468 }
469 }
470
Alex Crichton715862b2018-05-17 12:31:49 -0700471 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500472 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500473 Borrowed(ref n) => n.clone(),
474 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500475 }
476 }
477
Alex Crichton715862b2018-05-17 12:31:49 -0700478 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500479 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500480 Borrowed(ref n) => n.clone(),
481 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500482 }
483 }
484
Alex Crichton715862b2018-05-17 12:31:49 -0700485 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500486 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500487 Borrowed(ref n) => quote!(*#n),
488 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500489 }
490 }
491 }
492
Nika Layzellc08227a2017-12-04 16:30:17 -0500493 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400494 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500495 PathArguments::AngleBracketed(ref data) => data,
496 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400497 };
498
David Tolnay01ed0ce2018-05-20 20:18:14 -0700499 match **data
500 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500501 .first()
502 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800503 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500504 {
David Tolnayea9ae892017-12-26 01:44:32 -0500505 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500506 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500507 }
Nika Layzell27726662017-10-24 23:16:35 -0400508 }
509
David Tolnay6af48992018-08-01 11:16:28 -0700510 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
511 let ident = under_name(item.ast.ident.clone());
512
David Tolnay4a918742017-12-28 16:54:41 -0500513 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700514 Visit => {
515 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
516 let name = name.ref_tokens();
517 quote! {
518 _visitor.#method(#name)
519 }
520 }
521 VisitMut => {
522 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
523 let name = name.ref_mut_tokens();
524 quote! {
525 _visitor.#method(#name)
526 }
527 }
528 Fold => {
529 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
530 let name = name.owned_tokens();
531 quote! {
532 _visitor.#method(#name)
533 }
534 }
Nika Layzell27726662017-10-24 23:16:35 -0400535 }
536 }
537
David Tolnay6af48992018-08-01 11:16:28 -0700538 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500539 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500540 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500541 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700542 Fold => quote! {
543 Box::new(#res)
544 },
David Tolnay83db9272017-12-28 17:02:31 -0500545 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500546 })
Nika Layzell27726662017-10-24 23:16:35 -0400547 }
548
David Tolnay6af48992018-08-01 11:16:28 -0700549 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500550 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500551 Visit | VisitMut => Borrowed(quote!(it)),
552 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500553 };
David Tolnay39d0a202017-12-28 18:19:00 -0500554 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500555 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700556 Visit => {
557 let name = name.ref_tokens();
558 quote! {
559 for it in #name {
560 #val
561 }
562 }
563 }
564 VisitMut => {
565 let name = name.ref_mut_tokens();
566 quote! {
567 for it in #name {
568 #val
569 }
570 }
571 }
572 Fold => {
573 let name = name.owned_tokens();
574 quote! {
575 FoldHelper::lift(#name, |it| { #val })
576 }
577 }
David Tolnay3d772182017-12-28 17:18:53 -0500578 })
579 }
580
David Tolnay6eff4da2018-01-01 20:27:45 -0800581 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500582 elem: &Type,
583 lookup: &Lookup,
584 kind: Kind,
585 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700586 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500587 let operand = match kind {
588 Visit | VisitMut => Borrowed(quote!(it)),
589 Fold => Owned(quote!(it)),
590 };
David Tolnay39d0a202017-12-28 18:19:00 -0500591 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500592 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700593 Visit => {
594 let name = name.ref_tokens();
595 quote! {
596 for el in Punctuated::pairs(#name) {
597 let it = el.value();
598 #val
599 }
600 }
601 }
602 VisitMut => {
603 let name = name.ref_mut_tokens();
604 quote! {
605 for mut el in Punctuated::pairs_mut(#name) {
606 let it = el.value_mut();
607 #val
608 }
609 }
610 }
611 Fold => {
612 let name = name.owned_tokens();
613 quote! {
614 FoldHelper::lift(#name, |it| { #val })
615 }
616 }
David Tolnay4a918742017-12-28 16:54:41 -0500617 })
Nika Layzell27726662017-10-24 23:16:35 -0400618 }
619
David Tolnay6af48992018-08-01 11:16:28 -0700620 fn option_visit(
621 elem: &Type,
622 lookup: &Lookup,
623 kind: Kind,
624 name: &Operand,
625 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500626 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500627 Visit | VisitMut => Borrowed(quote!(it)),
628 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500629 };
David Tolnay39d0a202017-12-28 18:19:00 -0500630 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700631 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500632 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700633 Visit => quote! {
634 if let Some(ref it) = #name {
635 #val
636 }
637 },
638 VisitMut => quote! {
639 if let Some(ref mut it) = #name {
640 #val
641 }
642 },
643 Fold => quote! {
644 (#name).map(|it| { #val })
645 },
David Tolnay4a918742017-12-28 16:54:41 -0500646 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400647 }
648
David Tolnay5c4c0b52017-12-28 17:58:54 -0500649 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500650 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500651 lookup: &Lookup,
652 kind: Kind,
653 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700654 ) -> Option<TokenStream> {
655 if elems.is_empty() {
656 return None;
657 }
658
659 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500660 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500661 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500662 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500663 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700664 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700665 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500666 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700667 Fold => code.append_all(quote!(,)),
668 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500669 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500670 }
David Tolnay6af48992018-08-01 11:16:28 -0700671 Some(match kind {
672 Fold => quote! {
673 (#code)
674 },
675 Visit | VisitMut => code,
676 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500677 }
678
David Tolnay7ac699c2018-08-24 14:00:58 -0400679 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700680 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500681 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700682 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400683 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700684 },
685 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400686 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700687 },
688 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400689 tokens_helper(_visitor, &mut #name.spans)
690 },
691 }
692 }
693
694 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
695 let name = name.tokens();
696 match kind {
697 Fold => quote! {
698 #ty(tokens_helper(_visitor, &#name.span))
699 },
700 Visit => quote! {
701 tokens_helper(_visitor, &#name.span)
702 },
703 VisitMut => quote! {
704 tokens_helper(_visitor, &mut #name.span)
705 },
706 }
707 }
708
709 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
710 let name = name.tokens();
711 match kind {
712 Fold => quote! {
713 #ty(tokens_helper(_visitor, &#name.span))
714 },
715 Visit => quote! {
716 tokens_helper(_visitor, &#name.span)
717 },
718 VisitMut => quote! {
719 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700720 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500721 }
722 }
723
David Tolnay6af48992018-08-01 11:16:28 -0700724 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500725 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700726 Fold => name.owned_tokens(),
727 Visit | VisitMut => {
728 let name = name.tokens();
729 quote! {
730 skip!(#name)
731 }
732 }
David Tolnay4a918742017-12-28 16:54:41 -0500733 }
734 }
735
David Tolnay6af48992018-08-01 11:16:28 -0700736 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500737 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700738 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
739 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
740 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
741 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
742 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500743 RelevantType::Simple(item) => {
744 let mut res = simple_visit(item, kind, name);
745 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700746 quote! {
747 full!(#res)
748 }
David Tolnay3d772182017-12-28 17:18:53 -0500749 } else {
750 res
751 })
752 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400753 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
754 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
755 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700756 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400757 }
Nika Layzell27726662017-10-24 23:16:35 -0400758 }
759
760 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700761 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700762 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700763 let ty = &s.ast.ident;
764 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
765 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
766 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400767
David Tolnay6af48992018-08-01 11:16:28 -0700768 let mut visit_impl = TokenStream::new();
769 let mut visit_mut_impl = TokenStream::new();
770 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400771
David Tolnaye3d41b72017-12-31 15:24:00 -0500772 match s.ast.data {
773 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700774 let mut visit_variants = TokenStream::new();
775 let mut visit_mut_variants = TokenStream::new();
776 let mut fold_variants = TokenStream::new();
777
Nika Layzell27726662017-10-24 23:16:35 -0400778 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700779 let variant_ident = &variant.ident;
780
781 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500782 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
783 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700784 let mut bind_visit_fields = TokenStream::new();
785 let mut bind_visit_mut_fields = TokenStream::new();
786 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400787
David Tolnay6af48992018-08-01 11:16:28 -0700788 let mut visit_fields = TokenStream::new();
789 let mut visit_mut_fields = TokenStream::new();
790 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400791
David Tolnay6af48992018-08-01 11:16:28 -0700792 for (idx, field) in fields.unnamed.iter().enumerate() {
793 let name = format!("_binding_{}", idx);
794 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400795
David Tolnay6af48992018-08-01 11:16:28 -0700796 bind_visit_fields.append_all(quote! {
797 ref #binding,
798 });
799 bind_visit_mut_fields.append_all(quote! {
800 ref mut #binding,
801 });
802 bind_fold_fields.append_all(quote! {
803 #binding,
804 });
Nika Layzell27726662017-10-24 23:16:35 -0400805
David Tolnay6af48992018-08-01 11:16:28 -0700806 let borrowed_binding = Borrowed(quote!(#binding));
807 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400808
David Tolnay6af48992018-08-01 11:16:28 -0700809 visit_fields.append_all(
810 visit(&field.ty, lookup, Visit, &borrowed_binding)
811 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
812 );
813 visit_mut_fields.append_all(
814 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
815 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
816 );
817 fold_fields.append_all(
818 visit(&field.ty, lookup, Fold, &owned_binding)
819 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
820 );
Nika Layzell27726662017-10-24 23:16:35 -0400821
David Tolnay6af48992018-08-01 11:16:28 -0700822 visit_fields.append_all(quote!(;));
823 visit_mut_fields.append_all(quote!(;));
824 fold_fields.append_all(quote!(,));
825 }
Nika Layzell27726662017-10-24 23:16:35 -0400826
David Tolnay6af48992018-08-01 11:16:28 -0700827 visit_variants.append_all(quote! {
828 #ty::#variant_ident(#bind_visit_fields) => {
829 #visit_fields
830 }
831 });
832
833 visit_mut_variants.append_all(quote! {
834 #ty::#variant_ident(#bind_visit_mut_fields) => {
835 #visit_mut_fields
836 }
837 });
838
839 fold_variants.append_all(quote! {
840 #ty::#variant_ident(#bind_fold_fields) => {
841 #ty::#variant_ident(
842 #fold_fields
843 )
844 }
845 });
Nika Layzell27726662017-10-24 23:16:35 -0400846 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500847 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700848 visit_variants.append_all(quote! {
849 #ty::#variant_ident => {}
850 });
851 visit_mut_variants.append_all(quote! {
852 #ty::#variant_ident => {}
853 });
854 fold_variants.append_all(quote! {
855 #ty::#variant_ident => {
856 #ty::#variant_ident
857 }
858 });
Nika Layzell27726662017-10-24 23:16:35 -0400859 }
Nika Layzell27726662017-10-24 23:16:35 -0400860 }
Nika Layzell27726662017-10-24 23:16:35 -0400861 }
David Tolnay6af48992018-08-01 11:16:28 -0700862
863 visit_impl.append_all(quote! {
864 match *_i {
865 #visit_variants
866 }
867 });
868
869 visit_mut_impl.append_all(quote! {
870 match *_i {
871 #visit_mut_variants
872 }
873 });
874
875 fold_impl.append_all(quote! {
876 match _i {
877 #fold_variants
878 }
879 });
Nika Layzell27726662017-10-24 23:16:35 -0400880 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500881 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700882 let mut fold_fields = TokenStream::new();
883
David Tolnaybc5b2c02018-08-02 00:18:23 -0700884 for (idx, field) in v.fields.iter().enumerate() {
885 let id = match field.ident {
886 Some(ref ident) => Member::Named(ident.clone()),
887 None => Member::Unnamed(Index::from(idx)),
888 };
889 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700890 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
891 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
892 visit_impl.append_all(quote! {
893 #visit_field;
894 });
895 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
896 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
897 visit_mut_impl.append_all(quote! {
898 #visit_mut_field;
899 });
David Tolnay83db9272017-12-28 17:02:31 -0500900 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700901 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400902 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700903 fold_fields.append_all(quote! {
904 #name: #fold,
905 });
Nika Layzell27726662017-10-24 23:16:35 -0400906 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700907 fold_fields.append_all(quote! {
908 #fold,
909 });
Nika Layzell27726662017-10-24 23:16:35 -0400910 }
911 }
912
David Tolnaye3d41b72017-12-31 15:24:00 -0500913 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700914 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
915 #ty {
916 #fold_fields
917 }
918 }),
919 Fields::Unit => {
920 if ty == "Ident" {
921 fold_impl.append_all(quote! {
922 let mut _i = _i;
923 let span = _visitor.fold_span(_i.span());
924 _i.set_span(span);
925 });
926 }
927 fold_impl.append_all(quote! {
928 _i
929 });
930 }
Nika Layzell27726662017-10-24 23:16:35 -0400931 };
932 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500933 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400934 }
935
David Tolnay6af48992018-08-01 11:16:28 -0700936 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800937 if let Data::Struct(ref data) = s.ast.data {
938 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700939 if fields
940 .named
941 .iter()
942 .any(|field| field.vis == Visibility::Inherited)
943 {
David Tolnay360efd22018-01-04 23:35:26 -0800944 // Discard the generated impl if there are private fields.
945 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700946 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800947 }
948 }
David Tolnayd0adf522017-12-29 01:30:07 -0500949 }
David Tolnay6af48992018-08-01 11:16:28 -0700950
951 state.visit_trait.append_all(quote! {
952 #features
953 fn #visit_fn(&mut self, i: &'ast #ty) {
954 #visit_fn(self, i)
955 }
956 });
957
958 state.visit_impl.append_all(quote! {
959 #features
960 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
961 _visitor: &mut V, _i: &'ast #ty
962 ) {
963 #visit_impl
964 }
965 });
966
967 state.visit_mut_trait.append_all(quote! {
968 #features
969 fn #visit_mut_fn(&mut self, i: &mut #ty) {
970 #visit_mut_fn(self, i)
971 }
972 });
973
974 state.visit_mut_impl.append_all(quote! {
975 #features
976 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
977 _visitor: &mut V, _i: &mut #ty
978 ) {
979 #visit_mut_impl
980 }
981 });
982
983 state.fold_trait.append_all(quote! {
984 #features
985 fn #fold_fn(&mut self, i: #ty) -> #ty {
986 #fold_fn(self, i)
987 }
988 });
989
990 if include_fold_impl {
991 state.fold_impl.append_all(quote! {
992 #features
993 pub fn #fold_fn<V: Fold + ?Sized>(
994 _visitor: &mut V, _i: #ty
995 ) -> #ty {
996 #fold_impl
997 }
998 });
999 }
Nika Layzell27726662017-10-24 23:16:35 -04001000 }
1001}
1002
David Tolnay6af48992018-08-01 11:16:28 -07001003fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -07001004 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -07001005 write!(
1006 file,
1007 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
1008 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001009 let mut config = rustfmt::Config::default();
1010 config.set().emit_mode(rustfmt::EmitMode::Stdout);
1011 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -07001012 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001013 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001014 session
1015 .format(rustfmt::Input::Text(content.to_string()))
1016 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001017}
1018
Nika Layzell27726662017-10-24 23:16:35 -04001019fn main() {
1020 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001021 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001022
Nika Layzellefb83ba2017-12-19 18:23:55 -05001023 // Load in any terminal types
1024 for &tt in TERMINAL_TYPES {
1025 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001026 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001027 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001028 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001029 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001030 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001031 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001032 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001033 }),
1034 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001035 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001036 data: Data::Struct(DataStruct {
1037 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001038 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001039 semi_token: None,
1040 }),
1041 },
hcplaa511792018-05-29 07:13:01 +03001042 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001043 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001044 },
David Tolnayd67fb752017-12-27 13:50:29 -05001045 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001046 }
1047
David Tolnay6af48992018-08-01 11:16:28 -07001048 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001049 for s in lookup.values() {
1050 codegen::generate(&mut state, &lookup, s);
1051 }
1052
David Tolnay6af48992018-08-01 11:16:28 -07001053 let full_macro = quote! {
1054 #[cfg(feature = "full")]
1055 macro_rules! full {
1056 ($e:expr) => {
1057 $e
1058 };
1059 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001060
David Tolnay6af48992018-08-01 11:16:28 -07001061 #[cfg(all(feature = "derive", not(feature = "full")))]
1062 macro_rules! full {
1063 ($e:expr) => {
1064 unreachable!()
1065 };
1066 }
1067 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001068
David Tolnay6af48992018-08-01 11:16:28 -07001069 let skip_macro = quote! {
1070 #[cfg(any(feature = "full", feature = "derive"))]
1071 macro_rules! skip {
1072 ($($tt:tt)*) => {};
1073 }
1074 };
1075
1076 let fold_trait = state.fold_trait;
1077 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001078 write_file(
1079 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001080 quote! {
1081 // Unreachable code is generated sometimes without the full feature.
1082 #![allow(unreachable_code)]
1083 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001084
David Tolnay6af48992018-08-01 11:16:28 -07001085 use *;
1086 #[cfg(any(feature = "full", feature = "derive"))]
1087 use token::{Brace, Bracket, Paren, Group};
1088 use proc_macro2::Span;
1089 #[cfg(any(feature = "full", feature = "derive"))]
1090 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001091
David Tolnay6af48992018-08-01 11:16:28 -07001092 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001093
David Tolnay6af48992018-08-01 11:16:28 -07001094 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1095 ///
1096 /// See the [module documentation] for details.
1097 ///
1098 /// [module documentation]: index.html
1099 ///
1100 /// *This trait is available if Syn is built with the `"fold"` feature.*
1101 pub trait Fold {
1102 #fold_trait
1103 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001104
David Tolnay6af48992018-08-01 11:16:28 -07001105 #[cfg(any(feature = "full", feature = "derive"))]
1106 macro_rules! fold_span_only {
1107 ($f:ident : $t:ident) => {
1108 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1109 let span = _visitor.fold_span(_i.span());
1110 _i.set_span(span);
1111 _i
1112 }
1113 }
1114 }
Nika Layzell27726662017-10-24 23:16:35 -04001115
David Tolnay6af48992018-08-01 11:16:28 -07001116 #[cfg(any(feature = "full", feature = "derive"))]
1117 fold_span_only!(fold_lit_byte: LitByte);
1118 #[cfg(any(feature = "full", feature = "derive"))]
1119 fold_span_only!(fold_lit_byte_str: LitByteStr);
1120 #[cfg(any(feature = "full", feature = "derive"))]
1121 fold_span_only!(fold_lit_char: LitChar);
1122 #[cfg(any(feature = "full", feature = "derive"))]
1123 fold_span_only!(fold_lit_float: LitFloat);
1124 #[cfg(any(feature = "full", feature = "derive"))]
1125 fold_span_only!(fold_lit_int: LitInt);
1126 #[cfg(any(feature = "full", feature = "derive"))]
1127 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001128
David Tolnay6af48992018-08-01 11:16:28 -07001129 #fold_impl
1130 },
David Tolnayae0009e2018-08-01 00:40:00 -07001131 );
Nika Layzell27726662017-10-24 23:16:35 -04001132
David Tolnay6af48992018-08-01 11:16:28 -07001133 let visit_trait = state.visit_trait;
1134 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001135 write_file(
1136 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001137 quote! {
1138 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001139
David Tolnay6af48992018-08-01 11:16:28 -07001140 use *;
1141 #[cfg(any(feature = "full", feature = "derive"))]
1142 use punctuated::Punctuated;
1143 use proc_macro2::Span;
1144 #[cfg(any(feature = "full", feature = "derive"))]
1145 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001146
David Tolnay6af48992018-08-01 11:16:28 -07001147 #full_macro
1148 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001149
David Tolnay6af48992018-08-01 11:16:28 -07001150 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1151 ///
1152 /// See the [module documentation] for details.
1153 ///
1154 /// [module documentation]: index.html
1155 ///
1156 /// *This trait is available if Syn is built with the `"visit"` feature.*
1157 pub trait Visit<'ast> {
1158 #visit_trait
1159 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001160
David Tolnay6af48992018-08-01 11:16:28 -07001161 #visit_impl
1162 },
David Tolnayae0009e2018-08-01 00:40:00 -07001163 );
Nika Layzell27726662017-10-24 23:16:35 -04001164
David Tolnay6af48992018-08-01 11:16:28 -07001165 let visit_mut_trait = state.visit_mut_trait;
1166 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001167 write_file(
1168 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001169 quote! {
1170 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001171
David Tolnay6af48992018-08-01 11:16:28 -07001172 use *;
1173 #[cfg(any(feature = "full", feature = "derive"))]
1174 use punctuated::Punctuated;
1175 use proc_macro2::Span;
1176 #[cfg(any(feature = "full", feature = "derive"))]
1177 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001178
David Tolnay6af48992018-08-01 11:16:28 -07001179 #full_macro
1180 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001181
David Tolnay6af48992018-08-01 11:16:28 -07001182 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1183 /// place.
1184 ///
1185 /// See the [module documentation] for details.
1186 ///
1187 /// [module documentation]: index.html
1188 ///
1189 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1190 pub trait VisitMut {
1191 #visit_mut_trait
1192 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001193
David Tolnay6af48992018-08-01 11:16:28 -07001194 #visit_mut_impl
1195 },
David Tolnayae0009e2018-08-01 00:40:00 -07001196 );
Nika Layzell27726662017-10-24 23:16:35 -04001197}