blob: 4e2a60df33155260092bca4618a44cf1d6311d5e [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();
David Tolnay86cb9452018-08-31 10:35:09 -0700212 ahead.parse::<Token![#]>().is_ok() && ahead
213 .parse::<Ident>()
214 .map(|ident| ident == tag)
215 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700216 }
217
Nika Layzell27726662017-10-24 23:16:35 -0400218 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
219 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700220 fn full(input: ParseStream) -> (TokenStream, bool) {
221 if peek_tag(input, "full") {
222 input.parse::<Token![#]>().unwrap();
223 input.parse::<Ident>().unwrap();
224 (quote!(#[cfg(feature = "full")]), true)
225 } else {
226 (quote!(), false)
227 }
228 }
Nika Layzell27726662017-10-24 23:16:35 -0400229
David Tolnay64f03842018-08-30 21:34:40 -0700230 fn skip_manual_extra_traits(input: ParseStream) {
231 if peek_tag(input, "manual_extra_traits") {
232 input.parse::<Token![#]>().unwrap();
233 input.parse::<Ident>().unwrap();
234 }
235 }
David Tolnay28c5a462017-12-27 01:59:30 -0500236
Nika Layzell27726662017-10-24 23:16:35 -0400237 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700238 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
239 let ident: Ident = input.parse()?;
240 let (features, eos_full) = full(input);
241 skip_manual_extra_traits(input);
242 let rest: TokenStream = input.parse()?;
243 Ok(AstItem {
244 ast: syn::parse2(quote! {
245 pub struct #ident #rest
246 })?,
247 features: features,
248 eos_full: eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400249 })
David Tolnay64f03842018-08-30 21:34:40 -0700250 }
Nika Layzell27726662017-10-24 23:16:35 -0400251
252 // ast_struct! parsing
253 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700254 impl Parse for AstStruct {
255 fn parse(input: ParseStream) -> Result<Self> {
256 input.call(Attribute::parse_outer)?;
257 input.parse::<Token![pub]>()?;
258 input.parse::<Token![struct]>()?;
259 let res = input.call(ast_struct_inner)?;
260 Ok(AstStruct(vec![res]))
261 }
Nika Layzell27726662017-10-24 23:16:35 -0400262 }
263
David Tolnay64f03842018-08-30 21:34:40 -0700264 fn no_visit(input: ParseStream) -> bool {
265 if peek_tag(input, "no_visit") {
266 input.parse::<Token![#]>().unwrap();
267 input.parse::<Ident>().unwrap();
268 true
269 } else {
270 false
271 }
272 }
David Tolnay360efd22018-01-04 23:35:26 -0800273
Nika Layzell27726662017-10-24 23:16:35 -0400274 // ast_enum! parsing
275 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700276 impl Parse for AstEnum {
277 fn parse(input: ParseStream) -> Result<Self> {
278 input.call(Attribute::parse_outer)?;
279 input.parse::<Token![pub]>()?;
280 input.parse::<Token![enum]>()?;
281 let ident: Ident = input.parse()?;
282 let no_visit = no_visit(input);
283 let rest: TokenStream = input.parse()?;
284 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800285 vec![]
286 } else {
287 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700288 ast: syn::parse2(quote! {
289 pub enum #ident #rest
290 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800291 features: quote!(),
292 eos_full: false,
293 }]
294 }))
David Tolnay64f03842018-08-30 21:34:40 -0700295 }
Nika Layzell27726662017-10-24 23:16:35 -0400296 }
297
298 // A single variant of an ast_enum_of_structs!
299 struct EosVariant {
300 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500301 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400302 inner: Option<AstItem>,
303 }
David Tolnay64f03842018-08-30 21:34:40 -0700304 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
305 input.call(Attribute::parse_outer)?;
306 input.parse::<Token![pub]>()?;
307 let variant: Ident = input.parse()?;
308 let (member, inner) = if input.peek(token::Paren) {
309 let content;
310 parenthesized!(content in input);
311 if content.fork().call(ast_struct_inner).is_ok() {
312 let item = content.call(ast_struct_inner)?;
313 (Some(Path::from(item.ast.ident.clone())), Some(item))
314 } else {
315 let path: Path = content.parse()?;
316 (Some(path), None)
317 }
318 } else {
319 (None, None)
320 };
321 input.parse::<Token![,]>()?;
322 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400323 name: variant,
David Tolnay64f03842018-08-30 21:34:40 -0700324 member: member,
325 inner: inner,
Nika Layzell27726662017-10-24 23:16:35 -0400326 })
David Tolnay64f03842018-08-30 21:34:40 -0700327 }
Nika Layzell27726662017-10-24 23:16:35 -0400328
329 // ast_enum_of_structs! parsing
330 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700331 impl Parse for AstEnumOfStructs {
332 fn parse(input: ParseStream) -> Result<Self> {
333 input.call(Attribute::parse_outer)?;
334 input.parse::<Token![pub]>()?;
335 input.parse::<Token![enum]>()?;
336 let ident: Ident = input.parse()?;
337
338 let content;
339 braced!(content in input);
340 let mut variants = Vec::new();
341 while !content.is_empty() {
342 variants.push(content.call(eos_variant)?);
343 }
344
345 if let Some(ident) = input.parse::<Option<Ident>>()? {
346 assert_eq!(ident, "do_not_generate_to_tokens");
347 }
348
349 let enum_item = {
350 let variants = variants.iter().map(|v| {
351 let name = v.name.clone();
352 match v.member {
353 Some(ref member) => quote!(#name(#member)),
354 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700355 }
David Tolnay64f03842018-08-30 21:34:40 -0700356 });
357 parse_quote! {
358 pub enum #ident {
359 #(#variants),*
360 }
361 }
362 };
363 let mut items = vec![AstItem {
364 ast: enum_item,
365 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700366 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700367 }];
368 items.extend(variants.into_iter().filter_map(|v| v.inner));
369 Ok(AstEnumOfStructs(items))
370 }
Nika Layzell27726662017-10-24 23:16:35 -0400371 }
372}
373
374mod codegen {
375 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700376 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700377 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700378 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700379 use syn::ext::IdentExt;
380 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700381 use syn::punctuated::Punctuated;
382 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400383
384 #[derive(Default)]
385 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700386 pub visit_trait: TokenStream,
387 pub visit_impl: TokenStream,
388 pub visit_mut_trait: TokenStream,
389 pub visit_mut_impl: TokenStream,
390 pub fold_trait: TokenStream,
391 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400392 }
393
David Tolnay4a918742017-12-28 16:54:41 -0500394 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700395 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400396 }
397
David Tolnay3d772182017-12-28 17:18:53 -0500398 enum RelevantType<'a> {
399 Box(&'a Type),
400 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500401 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500402 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500403 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500404 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400405 TokenPunct(TokenStream),
406 TokenKeyword(TokenStream),
407 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500408 Pass,
409 }
Nika Layzell27726662017-10-24 23:16:35 -0400410
David Tolnay3d772182017-12-28 17:18:53 -0500411 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
412 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700413 Type::Path(TypePath {
414 qself: None,
415 ref path,
416 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800417 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700418 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500419 "Box" => RelevantType::Box(first_arg(&last.arguments)),
420 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500421 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500422 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500423 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400424 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500425 }
David Tolnay3d772182017-12-28 17:18:53 -0500426 _ => {
427 if let Some(item) = lookup.get(&last.ident) {
428 RelevantType::Simple(item)
429 } else {
430 RelevantType::Pass
431 }
432 }
433 }
Nika Layzell27726662017-10-24 23:16:35 -0400434 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700435 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
436 Type::Macro(TypeMacro { ref mac })
437 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
438 {
David Tolnay86cb9452018-08-31 10:35:09 -0700439 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400440 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
441 if is_ident && !is_underscore {
442 RelevantType::TokenKeyword(mac.into_token_stream())
443 } else {
444 RelevantType::TokenPunct(mac.into_token_stream())
445 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500446 }
David Tolnay3d772182017-12-28 17:18:53 -0500447 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400448 }
449 }
450
451 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
452 enum Kind {
453 Visit,
454 VisitMut,
455 Fold,
456 }
457
David Tolnayf0d63bf2017-12-26 12:29:47 -0500458 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700459 Borrowed(TokenStream),
460 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500461 }
462
David Tolnay83db9272017-12-28 17:02:31 -0500463 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700464 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500465
David Tolnayf0d63bf2017-12-26 12:29:47 -0500466 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700467 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500468 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500469 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500470 }
471 }
472
Alex Crichton715862b2018-05-17 12:31:49 -0700473 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500474 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500475 Borrowed(ref n) => n.clone(),
476 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500477 }
478 }
479
Alex Crichton715862b2018-05-17 12:31:49 -0700480 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500481 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500482 Borrowed(ref n) => n.clone(),
483 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500484 }
485 }
486
Alex Crichton715862b2018-05-17 12:31:49 -0700487 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500488 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500489 Borrowed(ref n) => quote!(*#n),
490 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500491 }
492 }
493 }
494
Nika Layzellc08227a2017-12-04 16:30:17 -0500495 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400496 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500497 PathArguments::AngleBracketed(ref data) => data,
498 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400499 };
500
David Tolnay01ed0ce2018-05-20 20:18:14 -0700501 match **data
502 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500503 .first()
504 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800505 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500506 {
David Tolnayea9ae892017-12-26 01:44:32 -0500507 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500508 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500509 }
Nika Layzell27726662017-10-24 23:16:35 -0400510 }
511
David Tolnay6af48992018-08-01 11:16:28 -0700512 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
513 let ident = under_name(item.ast.ident.clone());
514
David Tolnay4a918742017-12-28 16:54:41 -0500515 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700516 Visit => {
517 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
518 let name = name.ref_tokens();
519 quote! {
520 _visitor.#method(#name)
521 }
522 }
523 VisitMut => {
524 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
525 let name = name.ref_mut_tokens();
526 quote! {
527 _visitor.#method(#name)
528 }
529 }
530 Fold => {
531 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
532 let name = name.owned_tokens();
533 quote! {
534 _visitor.#method(#name)
535 }
536 }
Nika Layzell27726662017-10-24 23:16:35 -0400537 }
538 }
539
David Tolnay6af48992018-08-01 11:16:28 -0700540 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500541 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500542 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500543 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700544 Fold => quote! {
545 Box::new(#res)
546 },
David Tolnay83db9272017-12-28 17:02:31 -0500547 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500548 })
Nika Layzell27726662017-10-24 23:16:35 -0400549 }
550
David Tolnay6af48992018-08-01 11:16:28 -0700551 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500552 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500553 Visit | VisitMut => Borrowed(quote!(it)),
554 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500555 };
David Tolnay39d0a202017-12-28 18:19:00 -0500556 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500557 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700558 Visit => {
559 let name = name.ref_tokens();
560 quote! {
561 for it in #name {
562 #val
563 }
564 }
565 }
566 VisitMut => {
567 let name = name.ref_mut_tokens();
568 quote! {
569 for it in #name {
570 #val
571 }
572 }
573 }
574 Fold => {
575 let name = name.owned_tokens();
576 quote! {
577 FoldHelper::lift(#name, |it| { #val })
578 }
579 }
David Tolnay3d772182017-12-28 17:18:53 -0500580 })
581 }
582
David Tolnay6eff4da2018-01-01 20:27:45 -0800583 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500584 elem: &Type,
585 lookup: &Lookup,
586 kind: Kind,
587 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700588 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500589 let operand = match kind {
590 Visit | VisitMut => Borrowed(quote!(it)),
591 Fold => Owned(quote!(it)),
592 };
David Tolnay39d0a202017-12-28 18:19:00 -0500593 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500594 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700595 Visit => {
596 let name = name.ref_tokens();
597 quote! {
598 for el in Punctuated::pairs(#name) {
599 let it = el.value();
600 #val
601 }
602 }
603 }
604 VisitMut => {
605 let name = name.ref_mut_tokens();
606 quote! {
607 for mut el in Punctuated::pairs_mut(#name) {
608 let it = el.value_mut();
609 #val
610 }
611 }
612 }
613 Fold => {
614 let name = name.owned_tokens();
615 quote! {
616 FoldHelper::lift(#name, |it| { #val })
617 }
618 }
David Tolnay4a918742017-12-28 16:54:41 -0500619 })
Nika Layzell27726662017-10-24 23:16:35 -0400620 }
621
David Tolnay6af48992018-08-01 11:16:28 -0700622 fn option_visit(
623 elem: &Type,
624 lookup: &Lookup,
625 kind: Kind,
626 name: &Operand,
627 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500628 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500629 Visit | VisitMut => Borrowed(quote!(it)),
630 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500631 };
David Tolnay39d0a202017-12-28 18:19:00 -0500632 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700633 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500634 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700635 Visit => quote! {
636 if let Some(ref it) = #name {
637 #val
638 }
639 },
640 VisitMut => quote! {
641 if let Some(ref mut it) = #name {
642 #val
643 }
644 },
645 Fold => quote! {
646 (#name).map(|it| { #val })
647 },
David Tolnay4a918742017-12-28 16:54:41 -0500648 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400649 }
650
David Tolnay5c4c0b52017-12-28 17:58:54 -0500651 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500652 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500653 lookup: &Lookup,
654 kind: Kind,
655 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700656 ) -> Option<TokenStream> {
657 if elems.is_empty() {
658 return None;
659 }
660
661 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500662 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500663 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500664 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500665 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700666 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700667 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500668 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700669 Fold => code.append_all(quote!(,)),
670 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500671 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500672 }
David Tolnay6af48992018-08-01 11:16:28 -0700673 Some(match kind {
674 Fold => quote! {
675 (#code)
676 },
677 Visit | VisitMut => code,
678 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500679 }
680
David Tolnay7ac699c2018-08-24 14:00:58 -0400681 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700682 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500683 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700684 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400685 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700686 },
687 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400688 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700689 },
690 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400691 tokens_helper(_visitor, &mut #name.spans)
692 },
693 }
694 }
695
696 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
697 let name = name.tokens();
698 match kind {
699 Fold => quote! {
700 #ty(tokens_helper(_visitor, &#name.span))
701 },
702 Visit => quote! {
703 tokens_helper(_visitor, &#name.span)
704 },
705 VisitMut => quote! {
706 tokens_helper(_visitor, &mut #name.span)
707 },
708 }
709 }
710
711 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
712 let name = name.tokens();
713 match kind {
714 Fold => quote! {
715 #ty(tokens_helper(_visitor, &#name.span))
716 },
717 Visit => quote! {
718 tokens_helper(_visitor, &#name.span)
719 },
720 VisitMut => quote! {
721 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700722 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500723 }
724 }
725
David Tolnay6af48992018-08-01 11:16:28 -0700726 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500727 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700728 Fold => name.owned_tokens(),
729 Visit | VisitMut => {
730 let name = name.tokens();
731 quote! {
732 skip!(#name)
733 }
734 }
David Tolnay4a918742017-12-28 16:54:41 -0500735 }
736 }
737
David Tolnay6af48992018-08-01 11:16:28 -0700738 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500739 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700740 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
741 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
742 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
743 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
744 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500745 RelevantType::Simple(item) => {
746 let mut res = simple_visit(item, kind, name);
747 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700748 quote! {
749 full!(#res)
750 }
David Tolnay3d772182017-12-28 17:18:53 -0500751 } else {
752 res
753 })
754 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400755 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
756 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
757 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700758 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400759 }
Nika Layzell27726662017-10-24 23:16:35 -0400760 }
761
762 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700763 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700764 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700765 let ty = &s.ast.ident;
766 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
767 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
768 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400769
David Tolnay6af48992018-08-01 11:16:28 -0700770 let mut visit_impl = TokenStream::new();
771 let mut visit_mut_impl = TokenStream::new();
772 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400773
David Tolnaye3d41b72017-12-31 15:24:00 -0500774 match s.ast.data {
775 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700776 let mut visit_variants = TokenStream::new();
777 let mut visit_mut_variants = TokenStream::new();
778 let mut fold_variants = TokenStream::new();
779
Nika Layzell27726662017-10-24 23:16:35 -0400780 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700781 let variant_ident = &variant.ident;
782
783 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500784 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
785 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700786 let mut bind_visit_fields = TokenStream::new();
787 let mut bind_visit_mut_fields = TokenStream::new();
788 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400789
David Tolnay6af48992018-08-01 11:16:28 -0700790 let mut visit_fields = TokenStream::new();
791 let mut visit_mut_fields = TokenStream::new();
792 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400793
David Tolnay6af48992018-08-01 11:16:28 -0700794 for (idx, field) in fields.unnamed.iter().enumerate() {
795 let name = format!("_binding_{}", idx);
796 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400797
David Tolnay6af48992018-08-01 11:16:28 -0700798 bind_visit_fields.append_all(quote! {
799 ref #binding,
800 });
801 bind_visit_mut_fields.append_all(quote! {
802 ref mut #binding,
803 });
804 bind_fold_fields.append_all(quote! {
805 #binding,
806 });
Nika Layzell27726662017-10-24 23:16:35 -0400807
David Tolnay6af48992018-08-01 11:16:28 -0700808 let borrowed_binding = Borrowed(quote!(#binding));
809 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400810
David Tolnay6af48992018-08-01 11:16:28 -0700811 visit_fields.append_all(
812 visit(&field.ty, lookup, Visit, &borrowed_binding)
813 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
814 );
815 visit_mut_fields.append_all(
816 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
817 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
818 );
819 fold_fields.append_all(
820 visit(&field.ty, lookup, Fold, &owned_binding)
821 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
822 );
Nika Layzell27726662017-10-24 23:16:35 -0400823
David Tolnay6af48992018-08-01 11:16:28 -0700824 visit_fields.append_all(quote!(;));
825 visit_mut_fields.append_all(quote!(;));
826 fold_fields.append_all(quote!(,));
827 }
Nika Layzell27726662017-10-24 23:16:35 -0400828
David Tolnay6af48992018-08-01 11:16:28 -0700829 visit_variants.append_all(quote! {
830 #ty::#variant_ident(#bind_visit_fields) => {
831 #visit_fields
832 }
833 });
834
835 visit_mut_variants.append_all(quote! {
836 #ty::#variant_ident(#bind_visit_mut_fields) => {
837 #visit_mut_fields
838 }
839 });
840
841 fold_variants.append_all(quote! {
842 #ty::#variant_ident(#bind_fold_fields) => {
843 #ty::#variant_ident(
844 #fold_fields
845 )
846 }
847 });
Nika Layzell27726662017-10-24 23:16:35 -0400848 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500849 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700850 visit_variants.append_all(quote! {
851 #ty::#variant_ident => {}
852 });
853 visit_mut_variants.append_all(quote! {
854 #ty::#variant_ident => {}
855 });
856 fold_variants.append_all(quote! {
857 #ty::#variant_ident => {
858 #ty::#variant_ident
859 }
860 });
Nika Layzell27726662017-10-24 23:16:35 -0400861 }
Nika Layzell27726662017-10-24 23:16:35 -0400862 }
Nika Layzell27726662017-10-24 23:16:35 -0400863 }
David Tolnay6af48992018-08-01 11:16:28 -0700864
865 visit_impl.append_all(quote! {
866 match *_i {
867 #visit_variants
868 }
869 });
870
871 visit_mut_impl.append_all(quote! {
872 match *_i {
873 #visit_mut_variants
874 }
875 });
876
877 fold_impl.append_all(quote! {
878 match _i {
879 #fold_variants
880 }
881 });
Nika Layzell27726662017-10-24 23:16:35 -0400882 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500883 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700884 let mut fold_fields = TokenStream::new();
885
David Tolnaybc5b2c02018-08-02 00:18:23 -0700886 for (idx, field) in v.fields.iter().enumerate() {
887 let id = match field.ident {
888 Some(ref ident) => Member::Named(ident.clone()),
889 None => Member::Unnamed(Index::from(idx)),
890 };
891 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700892 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
893 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
894 visit_impl.append_all(quote! {
895 #visit_field;
896 });
897 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
898 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
899 visit_mut_impl.append_all(quote! {
900 #visit_mut_field;
901 });
David Tolnay83db9272017-12-28 17:02:31 -0500902 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700903 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400904 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700905 fold_fields.append_all(quote! {
906 #name: #fold,
907 });
Nika Layzell27726662017-10-24 23:16:35 -0400908 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700909 fold_fields.append_all(quote! {
910 #fold,
911 });
Nika Layzell27726662017-10-24 23:16:35 -0400912 }
913 }
914
David Tolnaye3d41b72017-12-31 15:24:00 -0500915 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700916 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
917 #ty {
918 #fold_fields
919 }
920 }),
921 Fields::Unit => {
922 if ty == "Ident" {
923 fold_impl.append_all(quote! {
924 let mut _i = _i;
925 let span = _visitor.fold_span(_i.span());
926 _i.set_span(span);
927 });
928 }
929 fold_impl.append_all(quote! {
930 _i
931 });
932 }
Nika Layzell27726662017-10-24 23:16:35 -0400933 };
934 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500935 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400936 }
937
David Tolnay6af48992018-08-01 11:16:28 -0700938 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800939 if let Data::Struct(ref data) = s.ast.data {
940 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700941 if fields
942 .named
943 .iter()
944 .any(|field| field.vis == Visibility::Inherited)
945 {
David Tolnay360efd22018-01-04 23:35:26 -0800946 // Discard the generated impl if there are private fields.
947 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700948 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800949 }
950 }
David Tolnayd0adf522017-12-29 01:30:07 -0500951 }
David Tolnay6af48992018-08-01 11:16:28 -0700952
953 state.visit_trait.append_all(quote! {
954 #features
955 fn #visit_fn(&mut self, i: &'ast #ty) {
956 #visit_fn(self, i)
957 }
958 });
959
960 state.visit_impl.append_all(quote! {
961 #features
962 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
963 _visitor: &mut V, _i: &'ast #ty
964 ) {
965 #visit_impl
966 }
967 });
968
969 state.visit_mut_trait.append_all(quote! {
970 #features
971 fn #visit_mut_fn(&mut self, i: &mut #ty) {
972 #visit_mut_fn(self, i)
973 }
974 });
975
976 state.visit_mut_impl.append_all(quote! {
977 #features
978 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
979 _visitor: &mut V, _i: &mut #ty
980 ) {
981 #visit_mut_impl
982 }
983 });
984
985 state.fold_trait.append_all(quote! {
986 #features
987 fn #fold_fn(&mut self, i: #ty) -> #ty {
988 #fold_fn(self, i)
989 }
990 });
991
992 if include_fold_impl {
993 state.fold_impl.append_all(quote! {
994 #features
995 pub fn #fold_fn<V: Fold + ?Sized>(
996 _visitor: &mut V, _i: #ty
997 ) -> #ty {
998 #fold_impl
999 }
1000 });
1001 }
Nika Layzell27726662017-10-24 23:16:35 -04001002 }
1003}
1004
David Tolnay6af48992018-08-01 11:16:28 -07001005fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -07001006 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -07001007 write!(
1008 file,
1009 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
1010 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001011 let mut config = rustfmt::Config::default();
1012 config.set().emit_mode(rustfmt::EmitMode::Stdout);
1013 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -07001014 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001015 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001016 session
1017 .format(rustfmt::Input::Text(content.to_string()))
1018 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001019}
1020
Nika Layzell27726662017-10-24 23:16:35 -04001021fn main() {
1022 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001023 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001024
Nika Layzellefb83ba2017-12-19 18:23:55 -05001025 // Load in any terminal types
1026 for &tt in TERMINAL_TYPES {
1027 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001028 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001029 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001030 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001031 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001032 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001033 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001034 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001035 }),
1036 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001037 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001038 data: Data::Struct(DataStruct {
1039 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001040 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001041 semi_token: None,
1042 }),
1043 },
hcplaa511792018-05-29 07:13:01 +03001044 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001045 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001046 },
David Tolnayd67fb752017-12-27 13:50:29 -05001047 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001048 }
1049
David Tolnay6af48992018-08-01 11:16:28 -07001050 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001051 for s in lookup.values() {
1052 codegen::generate(&mut state, &lookup, s);
1053 }
1054
David Tolnay6af48992018-08-01 11:16:28 -07001055 let full_macro = quote! {
1056 #[cfg(feature = "full")]
1057 macro_rules! full {
1058 ($e:expr) => {
1059 $e
1060 };
1061 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001062
David Tolnay6af48992018-08-01 11:16:28 -07001063 #[cfg(all(feature = "derive", not(feature = "full")))]
1064 macro_rules! full {
1065 ($e:expr) => {
1066 unreachable!()
1067 };
1068 }
1069 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001070
David Tolnay6af48992018-08-01 11:16:28 -07001071 let skip_macro = quote! {
1072 #[cfg(any(feature = "full", feature = "derive"))]
1073 macro_rules! skip {
1074 ($($tt:tt)*) => {};
1075 }
1076 };
1077
1078 let fold_trait = state.fold_trait;
1079 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001080 write_file(
1081 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001082 quote! {
1083 // Unreachable code is generated sometimes without the full feature.
1084 #![allow(unreachable_code)]
1085 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001086
David Tolnay6af48992018-08-01 11:16:28 -07001087 use *;
1088 #[cfg(any(feature = "full", feature = "derive"))]
1089 use token::{Brace, Bracket, Paren, Group};
1090 use proc_macro2::Span;
1091 #[cfg(any(feature = "full", feature = "derive"))]
1092 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001093
David Tolnay6af48992018-08-01 11:16:28 -07001094 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001095
David Tolnay6af48992018-08-01 11:16:28 -07001096 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1097 ///
1098 /// See the [module documentation] for details.
1099 ///
1100 /// [module documentation]: index.html
1101 ///
1102 /// *This trait is available if Syn is built with the `"fold"` feature.*
1103 pub trait Fold {
1104 #fold_trait
1105 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001106
David Tolnay6af48992018-08-01 11:16:28 -07001107 #[cfg(any(feature = "full", feature = "derive"))]
1108 macro_rules! fold_span_only {
1109 ($f:ident : $t:ident) => {
1110 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1111 let span = _visitor.fold_span(_i.span());
1112 _i.set_span(span);
1113 _i
1114 }
1115 }
1116 }
Nika Layzell27726662017-10-24 23:16:35 -04001117
David Tolnay6af48992018-08-01 11:16:28 -07001118 #[cfg(any(feature = "full", feature = "derive"))]
1119 fold_span_only!(fold_lit_byte: LitByte);
1120 #[cfg(any(feature = "full", feature = "derive"))]
1121 fold_span_only!(fold_lit_byte_str: LitByteStr);
1122 #[cfg(any(feature = "full", feature = "derive"))]
1123 fold_span_only!(fold_lit_char: LitChar);
1124 #[cfg(any(feature = "full", feature = "derive"))]
1125 fold_span_only!(fold_lit_float: LitFloat);
1126 #[cfg(any(feature = "full", feature = "derive"))]
1127 fold_span_only!(fold_lit_int: LitInt);
1128 #[cfg(any(feature = "full", feature = "derive"))]
1129 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001130
David Tolnay6af48992018-08-01 11:16:28 -07001131 #fold_impl
1132 },
David Tolnayae0009e2018-08-01 00:40:00 -07001133 );
Nika Layzell27726662017-10-24 23:16:35 -04001134
David Tolnay6af48992018-08-01 11:16:28 -07001135 let visit_trait = state.visit_trait;
1136 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001137 write_file(
1138 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001139 quote! {
1140 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001141
David Tolnay6af48992018-08-01 11:16:28 -07001142 use *;
1143 #[cfg(any(feature = "full", feature = "derive"))]
1144 use punctuated::Punctuated;
1145 use proc_macro2::Span;
1146 #[cfg(any(feature = "full", feature = "derive"))]
1147 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001148
David Tolnay6af48992018-08-01 11:16:28 -07001149 #full_macro
1150 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001151
David Tolnay6af48992018-08-01 11:16:28 -07001152 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1153 ///
1154 /// See the [module documentation] for details.
1155 ///
1156 /// [module documentation]: index.html
1157 ///
1158 /// *This trait is available if Syn is built with the `"visit"` feature.*
1159 pub trait Visit<'ast> {
1160 #visit_trait
1161 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001162
David Tolnay6af48992018-08-01 11:16:28 -07001163 #visit_impl
1164 },
David Tolnayae0009e2018-08-01 00:40:00 -07001165 );
Nika Layzell27726662017-10-24 23:16:35 -04001166
David Tolnay6af48992018-08-01 11:16:28 -07001167 let visit_mut_trait = state.visit_mut_trait;
1168 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001169 write_file(
1170 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001171 quote! {
1172 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001173
David Tolnay6af48992018-08-01 11:16:28 -07001174 use *;
1175 #[cfg(any(feature = "full", feature = "derive"))]
1176 use punctuated::Punctuated;
1177 use proc_macro2::Span;
1178 #[cfg(any(feature = "full", feature = "derive"))]
1179 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001180
David Tolnay6af48992018-08-01 11:16:28 -07001181 #full_macro
1182 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001183
David Tolnay6af48992018-08-01 11:16:28 -07001184 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1185 /// place.
1186 ///
1187 /// See the [module documentation] for details.
1188 ///
1189 /// [module documentation]: index.html
1190 ///
1191 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1192 pub trait VisitMut {
1193 #visit_mut_trait
1194 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001195
David Tolnay6af48992018-08-01 11:16:28 -07001196 #visit_mut_impl
1197 },
David Tolnayae0009e2018-08-01 00:40:00 -07001198 );
Nika Layzell27726662017-10-24 23:16:35 -04001199}