blob: 2ef5272b937b4b9c9157a1964b0f68e0a2dfceac [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 Tolnayc8d3e4a2018-11-21 01:36:58 -080014#![allow(
15 clippy::needless_pass_by_value,
16 clippy::redundant_closure,
17 clippy::write_with_newline,
David Tolnay6b46a702018-08-01 23:51:32 -070018)]
David Tolnayea9ae892017-12-26 01:44:32 -050019
David Tolnayd67fb752017-12-27 13:50:29 -050020#[macro_use]
21extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040022extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050023extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050024#[macro_use]
25extern crate quote;
26extern crate syn;
David Tolnay8c81f622018-07-31 23:34:35 -070027extern crate rustfmt_nightly as rustfmt;
Nika Layzell27726662017-10-24 23:16:35 -040028
David Tolnayd67fb752017-12-27 13:50:29 -050029use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070030use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070031use quote::ToTokens;
32use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040033
David Tolnay01ed0ce2018-05-20 20:18:14 -070034use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050035use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040036use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070037use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040038use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040039
40const SYN_CRATE_ROOT: &str = "../src/lib.rs";
41
42const FOLD_SRC: &str = "../src/gen/fold.rs";
43const VISIT_SRC: &str = "../src/gen/visit.rs";
44const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
45
David Tolnayd67fb752017-12-27 13:50:29 -050046const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040047
Alex Crichton131308c2018-05-18 14:00:24 -070048const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050049
Alex Crichtond261d092018-05-18 13:47:35 -070050const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050051
Alex Crichtona74a1c82018-05-16 10:20:44 -070052fn path_eq(a: &syn::Path, b: &str) -> bool {
David Tolnay78b16142018-09-01 16:53:07 -070053 a.leading_colon.is_none()
54 && a.segments.len() == 1
55 && a.segments[0].ident == b
Nika Layzell27726662017-10-24 23:16:35 -040056}
57
Alex Crichton715862b2018-05-17 12:31:49 -070058fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040059 for attr in attrs {
Alex Crichtona74a1c82018-05-16 10:20:44 -070060 if path_eq(&attr.path, "cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040061 attr.to_tokens(&mut features);
62 }
63 }
64 features
65}
66
67#[derive(Clone)]
68pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050069 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070070 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040071 // True if this is an ast_enum_of_structs! item with a #full annotation.
72 eos_full: bool,
73}
74
David Tolnayf0d63bf2017-12-26 12:29:47 -050075impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040076 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050078 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040079 .field("features", &self.features.to_string())
80 .finish()
81 }
82}
83
84// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050085type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040086
David Tolnay01ed0ce2018-05-20 20:18:14 -070087fn load_file<P: AsRef<Path>>(
88 name: P,
89 features: &TokenStream,
90 lookup: &mut Lookup,
91) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040092 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050093 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040094
95 let mut f = File::open(name)?;
96 let mut src = String::new();
97 f.read_to_string(&mut src)?;
98
99 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -0500100 let file =
101 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -0400102
103 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -0500104 'items: for item in file.items {
105 match item {
106 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400107 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800108 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400109 continue;
110 }
111
112 // We don't want to try to load the generated rust files and
113 // parse them, so we ignore them here.
114 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700115 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400116 continue 'items;
117 }
118 }
119
120 // Lookup any #[cfg()] attributes on the module and add them to
121 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800122 //
123 // The derive module is weird because it is built with either
124 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700125 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800126 quote!(#[cfg(feature = "derive")])
127 } else {
128 get_features(&item.attrs, features.clone())
129 };
Nika Layzell27726662017-10-24 23:16:35 -0400130
131 // Look up the submodule file, and recursively parse it.
132 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700133 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500134 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400135 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500136 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400137 // Lookip any #[cfg()] attributes directly on the macro
138 // invocation, and add them to the feature set.
139 let features = get_features(&item.attrs, features.clone());
140
141 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800142 let tts = &item.mac.tts;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700143 let found = if path_eq(&item.mac.path, "ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700144 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500145 .map_err(|_| err_msg("failed to parse ast_struct"))?
146 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700147 } else if path_eq(&item.mac.path, "ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700148 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500149 .map_err(|_| err_msg("failed to parse ast_enum"))?
150 .0
Alex Crichtona74a1c82018-05-16 10:20:44 -0700151 } else if path_eq(&item.mac.path, "ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700152 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500153 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500154 .0
Nika Layzell27726662017-10-24 23:16:35 -0400155 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500156 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400157 };
158
159 // Record our features on the parsed AstItems.
160 for mut item in found {
161 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700162 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400163 }
164 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500165 Item::Struct(item) => {
166 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700167 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700168 lookup.insert(
169 ident.clone(),
170 AstItem {
171 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700172 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700173 vis: item.vis,
174 attrs: item.attrs,
175 generics: item.generics,
176 data: Data::Struct(DataStruct {
177 fields: item.fields,
178 struct_token: item.struct_token,
179 semi_token: item.semi_token,
180 }),
181 },
182 features: features.clone(),
183 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500184 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700185 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500186 }
187 }
Nika Layzell27726662017-10-24 23:16:35 -0400188 _ => {}
189 }
190 }
191 Ok(())
192}
193
194mod parsing {
195 use super::AstItem;
196
David Tolnay01ed0ce2018-05-20 20:18:14 -0700197 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500198 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700199 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400200 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400201
David Tolnay64f03842018-08-30 21:34:40 -0700202 fn peek_tag(input: ParseStream, tag: &str) -> bool {
203 let ahead = input.fork();
David Tolnay86cb9452018-08-31 10:35:09 -0700204 ahead.parse::<Token![#]>().is_ok() && ahead
205 .parse::<Ident>()
206 .map(|ident| ident == tag)
207 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700208 }
209
Nika Layzell27726662017-10-24 23:16:35 -0400210 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
211 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700212 fn full(input: ParseStream) -> (TokenStream, bool) {
213 if peek_tag(input, "full") {
214 input.parse::<Token![#]>().unwrap();
215 input.parse::<Ident>().unwrap();
216 (quote!(#[cfg(feature = "full")]), true)
217 } else {
218 (quote!(), false)
219 }
220 }
Nika Layzell27726662017-10-24 23:16:35 -0400221
David Tolnay64f03842018-08-30 21:34:40 -0700222 fn skip_manual_extra_traits(input: ParseStream) {
223 if peek_tag(input, "manual_extra_traits") {
224 input.parse::<Token![#]>().unwrap();
225 input.parse::<Ident>().unwrap();
226 }
227 }
David Tolnay28c5a462017-12-27 01:59:30 -0500228
Nika Layzell27726662017-10-24 23:16:35 -0400229 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700230 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
231 let ident: Ident = input.parse()?;
232 let (features, eos_full) = full(input);
233 skip_manual_extra_traits(input);
234 let rest: TokenStream = input.parse()?;
235 Ok(AstItem {
236 ast: syn::parse2(quote! {
237 pub struct #ident #rest
238 })?,
David Tolnay9a518142018-11-21 01:38:50 -0800239 features,
240 eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400241 })
David Tolnay64f03842018-08-30 21:34:40 -0700242 }
Nika Layzell27726662017-10-24 23:16:35 -0400243
244 // ast_struct! parsing
245 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700246 impl Parse for AstStruct {
247 fn parse(input: ParseStream) -> Result<Self> {
248 input.call(Attribute::parse_outer)?;
249 input.parse::<Token![pub]>()?;
250 input.parse::<Token![struct]>()?;
251 let res = input.call(ast_struct_inner)?;
252 Ok(AstStruct(vec![res]))
253 }
Nika Layzell27726662017-10-24 23:16:35 -0400254 }
255
David Tolnay64f03842018-08-30 21:34:40 -0700256 fn no_visit(input: ParseStream) -> bool {
257 if peek_tag(input, "no_visit") {
258 input.parse::<Token![#]>().unwrap();
259 input.parse::<Ident>().unwrap();
260 true
261 } else {
262 false
263 }
264 }
David Tolnay360efd22018-01-04 23:35:26 -0800265
Nika Layzell27726662017-10-24 23:16:35 -0400266 // ast_enum! parsing
267 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700268 impl Parse for AstEnum {
269 fn parse(input: ParseStream) -> Result<Self> {
270 input.call(Attribute::parse_outer)?;
271 input.parse::<Token![pub]>()?;
272 input.parse::<Token![enum]>()?;
273 let ident: Ident = input.parse()?;
274 let no_visit = no_visit(input);
275 let rest: TokenStream = input.parse()?;
276 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800277 vec![]
278 } else {
279 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700280 ast: syn::parse2(quote! {
281 pub enum #ident #rest
282 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800283 features: quote!(),
284 eos_full: false,
285 }]
286 }))
David Tolnay64f03842018-08-30 21:34:40 -0700287 }
Nika Layzell27726662017-10-24 23:16:35 -0400288 }
289
290 // A single variant of an ast_enum_of_structs!
291 struct EosVariant {
292 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500293 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400294 inner: Option<AstItem>,
295 }
David Tolnay64f03842018-08-30 21:34:40 -0700296 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
297 input.call(Attribute::parse_outer)?;
298 input.parse::<Token![pub]>()?;
299 let variant: Ident = input.parse()?;
300 let (member, inner) = if input.peek(token::Paren) {
301 let content;
302 parenthesized!(content in input);
303 if content.fork().call(ast_struct_inner).is_ok() {
304 let item = content.call(ast_struct_inner)?;
305 (Some(Path::from(item.ast.ident.clone())), Some(item))
306 } else {
307 let path: Path = content.parse()?;
308 (Some(path), None)
309 }
310 } else {
311 (None, None)
312 };
313 input.parse::<Token![,]>()?;
314 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400315 name: variant,
David Tolnay9a518142018-11-21 01:38:50 -0800316 member,
317 inner,
Nika Layzell27726662017-10-24 23:16:35 -0400318 })
David Tolnay64f03842018-08-30 21:34:40 -0700319 }
Nika Layzell27726662017-10-24 23:16:35 -0400320
321 // ast_enum_of_structs! parsing
322 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700323 impl Parse for AstEnumOfStructs {
324 fn parse(input: ParseStream) -> Result<Self> {
325 input.call(Attribute::parse_outer)?;
326 input.parse::<Token![pub]>()?;
327 input.parse::<Token![enum]>()?;
328 let ident: Ident = input.parse()?;
329
330 let content;
331 braced!(content in input);
332 let mut variants = Vec::new();
333 while !content.is_empty() {
334 variants.push(content.call(eos_variant)?);
335 }
336
337 if let Some(ident) = input.parse::<Option<Ident>>()? {
338 assert_eq!(ident, "do_not_generate_to_tokens");
339 }
340
341 let enum_item = {
342 let variants = variants.iter().map(|v| {
343 let name = v.name.clone();
344 match v.member {
345 Some(ref member) => quote!(#name(#member)),
346 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700347 }
David Tolnay64f03842018-08-30 21:34:40 -0700348 });
349 parse_quote! {
350 pub enum #ident {
351 #(#variants),*
352 }
353 }
354 };
355 let mut items = vec![AstItem {
356 ast: enum_item,
357 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700358 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700359 }];
360 items.extend(variants.into_iter().filter_map(|v| v.inner));
361 Ok(AstEnumOfStructs(items))
362 }
Nika Layzell27726662017-10-24 23:16:35 -0400363 }
364}
365
366mod codegen {
367 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700368 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700369 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700370 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700371 use syn::ext::IdentExt;
372 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700373 use syn::punctuated::Punctuated;
374 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400375
376 #[derive(Default)]
377 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700378 pub visit_trait: TokenStream,
379 pub visit_impl: TokenStream,
380 pub visit_mut_trait: TokenStream,
381 pub visit_mut_impl: TokenStream,
382 pub fold_trait: TokenStream,
383 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400384 }
385
David Tolnay4a918742017-12-28 16:54:41 -0500386 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700387 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400388 }
389
David Tolnay3d772182017-12-28 17:18:53 -0500390 enum RelevantType<'a> {
391 Box(&'a Type),
392 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500393 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500394 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500395 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500396 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400397 TokenPunct(TokenStream),
398 TokenKeyword(TokenStream),
399 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500400 Pass,
401 }
Nika Layzell27726662017-10-24 23:16:35 -0400402
David Tolnay3d772182017-12-28 17:18:53 -0500403 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
404 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700405 Type::Path(TypePath {
406 qself: None,
407 ref path,
408 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800409 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700410 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500411 "Box" => RelevantType::Box(first_arg(&last.arguments)),
412 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500413 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500414 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500415 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400416 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500417 }
David Tolnay3d772182017-12-28 17:18:53 -0500418 _ => {
419 if let Some(item) = lookup.get(&last.ident) {
420 RelevantType::Simple(item)
421 } else {
422 RelevantType::Pass
423 }
424 }
425 }
Nika Layzell27726662017-10-24 23:16:35 -0400426 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700427 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
428 Type::Macro(TypeMacro { ref mac })
429 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
430 {
David Tolnay86cb9452018-08-31 10:35:09 -0700431 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400432 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
433 if is_ident && !is_underscore {
434 RelevantType::TokenKeyword(mac.into_token_stream())
435 } else {
436 RelevantType::TokenPunct(mac.into_token_stream())
437 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500438 }
David Tolnay3d772182017-12-28 17:18:53 -0500439 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400440 }
441 }
442
443 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
444 enum Kind {
445 Visit,
446 VisitMut,
447 Fold,
448 }
449
David Tolnayf0d63bf2017-12-26 12:29:47 -0500450 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700451 Borrowed(TokenStream),
452 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500453 }
454
David Tolnay83db9272017-12-28 17:02:31 -0500455 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700456 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500457
David Tolnayf0d63bf2017-12-26 12:29:47 -0500458 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700459 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500460 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500461 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500462 }
463 }
464
Alex Crichton715862b2018-05-17 12:31:49 -0700465 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500466 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500467 Borrowed(ref n) => n.clone(),
468 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500469 }
470 }
471
Alex Crichton715862b2018-05-17 12:31:49 -0700472 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500473 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500474 Borrowed(ref n) => n.clone(),
475 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500476 }
477 }
478
Alex Crichton715862b2018-05-17 12:31:49 -0700479 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500480 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500481 Borrowed(ref n) => quote!(*#n),
482 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500483 }
484 }
485 }
486
Nika Layzellc08227a2017-12-04 16:30:17 -0500487 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400488 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500489 PathArguments::AngleBracketed(ref data) => data,
490 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400491 };
492
David Tolnay01ed0ce2018-05-20 20:18:14 -0700493 match **data
494 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500495 .first()
496 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800497 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500498 {
David Tolnayea9ae892017-12-26 01:44:32 -0500499 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500500 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500501 }
Nika Layzell27726662017-10-24 23:16:35 -0400502 }
503
David Tolnay6af48992018-08-01 11:16:28 -0700504 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
505 let ident = under_name(item.ast.ident.clone());
506
David Tolnay4a918742017-12-28 16:54:41 -0500507 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700508 Visit => {
509 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
510 let name = name.ref_tokens();
511 quote! {
512 _visitor.#method(#name)
513 }
514 }
515 VisitMut => {
516 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
517 let name = name.ref_mut_tokens();
518 quote! {
519 _visitor.#method(#name)
520 }
521 }
522 Fold => {
523 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
524 let name = name.owned_tokens();
525 quote! {
526 _visitor.#method(#name)
527 }
528 }
Nika Layzell27726662017-10-24 23:16:35 -0400529 }
530 }
531
David Tolnay6af48992018-08-01 11:16:28 -0700532 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500533 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500534 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500535 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700536 Fold => quote! {
537 Box::new(#res)
538 },
David Tolnay83db9272017-12-28 17:02:31 -0500539 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500540 })
Nika Layzell27726662017-10-24 23:16:35 -0400541 }
542
David Tolnay6af48992018-08-01 11:16:28 -0700543 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500544 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500545 Visit | VisitMut => Borrowed(quote!(it)),
546 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500547 };
David Tolnay39d0a202017-12-28 18:19:00 -0500548 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500549 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700550 Visit => {
551 let name = name.ref_tokens();
552 quote! {
553 for it in #name {
554 #val
555 }
556 }
557 }
558 VisitMut => {
559 let name = name.ref_mut_tokens();
560 quote! {
561 for it in #name {
562 #val
563 }
564 }
565 }
566 Fold => {
567 let name = name.owned_tokens();
568 quote! {
569 FoldHelper::lift(#name, |it| { #val })
570 }
571 }
David Tolnay3d772182017-12-28 17:18:53 -0500572 })
573 }
574
David Tolnay6eff4da2018-01-01 20:27:45 -0800575 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500576 elem: &Type,
577 lookup: &Lookup,
578 kind: Kind,
579 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700580 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500581 let operand = match kind {
582 Visit | VisitMut => Borrowed(quote!(it)),
583 Fold => Owned(quote!(it)),
584 };
David Tolnay39d0a202017-12-28 18:19:00 -0500585 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500586 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700587 Visit => {
588 let name = name.ref_tokens();
589 quote! {
590 for el in Punctuated::pairs(#name) {
591 let it = el.value();
592 #val
593 }
594 }
595 }
596 VisitMut => {
597 let name = name.ref_mut_tokens();
598 quote! {
599 for mut el in Punctuated::pairs_mut(#name) {
600 let it = el.value_mut();
601 #val
602 }
603 }
604 }
605 Fold => {
606 let name = name.owned_tokens();
607 quote! {
608 FoldHelper::lift(#name, |it| { #val })
609 }
610 }
David Tolnay4a918742017-12-28 16:54:41 -0500611 })
Nika Layzell27726662017-10-24 23:16:35 -0400612 }
613
David Tolnay6af48992018-08-01 11:16:28 -0700614 fn option_visit(
615 elem: &Type,
616 lookup: &Lookup,
617 kind: Kind,
618 name: &Operand,
619 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500620 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500621 Visit | VisitMut => Borrowed(quote!(it)),
622 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500623 };
David Tolnay39d0a202017-12-28 18:19:00 -0500624 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700625 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500626 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700627 Visit => quote! {
628 if let Some(ref it) = #name {
629 #val
630 }
631 },
632 VisitMut => quote! {
633 if let Some(ref mut it) = #name {
634 #val
635 }
636 },
637 Fold => quote! {
638 (#name).map(|it| { #val })
639 },
David Tolnay4a918742017-12-28 16:54:41 -0500640 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400641 }
642
David Tolnay5c4c0b52017-12-28 17:58:54 -0500643 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500644 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500645 lookup: &Lookup,
646 kind: Kind,
647 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700648 ) -> Option<TokenStream> {
649 if elems.is_empty() {
650 return None;
651 }
652
653 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500654 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500655 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500656 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500657 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700658 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700659 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500660 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700661 Fold => code.append_all(quote!(,)),
662 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500663 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500664 }
David Tolnay6af48992018-08-01 11:16:28 -0700665 Some(match kind {
666 Fold => quote! {
667 (#code)
668 },
669 Visit | VisitMut => code,
670 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500671 }
672
David Tolnay7ac699c2018-08-24 14:00:58 -0400673 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700674 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500675 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700676 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400677 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700678 },
679 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400680 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700681 },
682 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400683 tokens_helper(_visitor, &mut #name.spans)
684 },
685 }
686 }
687
688 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
689 let name = name.tokens();
690 match kind {
691 Fold => quote! {
692 #ty(tokens_helper(_visitor, &#name.span))
693 },
694 Visit => quote! {
695 tokens_helper(_visitor, &#name.span)
696 },
697 VisitMut => quote! {
698 tokens_helper(_visitor, &mut #name.span)
699 },
700 }
701 }
702
703 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
704 let name = name.tokens();
705 match kind {
706 Fold => quote! {
707 #ty(tokens_helper(_visitor, &#name.span))
708 },
709 Visit => quote! {
710 tokens_helper(_visitor, &#name.span)
711 },
712 VisitMut => quote! {
713 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700714 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500715 }
716 }
717
David Tolnay6af48992018-08-01 11:16:28 -0700718 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500719 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700720 Fold => name.owned_tokens(),
721 Visit | VisitMut => {
722 let name = name.tokens();
723 quote! {
724 skip!(#name)
725 }
726 }
David Tolnay4a918742017-12-28 16:54:41 -0500727 }
728 }
729
David Tolnay6af48992018-08-01 11:16:28 -0700730 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500731 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700732 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
733 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
734 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
735 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
736 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500737 RelevantType::Simple(item) => {
738 let mut res = simple_visit(item, kind, name);
739 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700740 quote! {
741 full!(#res)
742 }
David Tolnay3d772182017-12-28 17:18:53 -0500743 } else {
744 res
745 })
746 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400747 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
748 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
749 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700750 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400751 }
Nika Layzell27726662017-10-24 23:16:35 -0400752 }
753
754 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700755 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700756 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700757 let ty = &s.ast.ident;
758 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
759 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
760 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400761
David Tolnay6af48992018-08-01 11:16:28 -0700762 let mut visit_impl = TokenStream::new();
763 let mut visit_mut_impl = TokenStream::new();
764 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400765
David Tolnaye3d41b72017-12-31 15:24:00 -0500766 match s.ast.data {
767 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700768 let mut visit_variants = TokenStream::new();
769 let mut visit_mut_variants = TokenStream::new();
770 let mut fold_variants = TokenStream::new();
771
Nika Layzell27726662017-10-24 23:16:35 -0400772 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700773 let variant_ident = &variant.ident;
774
775 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500776 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
777 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700778 let mut bind_visit_fields = TokenStream::new();
779 let mut bind_visit_mut_fields = TokenStream::new();
780 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400781
David Tolnay6af48992018-08-01 11:16:28 -0700782 let mut visit_fields = TokenStream::new();
783 let mut visit_mut_fields = TokenStream::new();
784 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400785
David Tolnay6af48992018-08-01 11:16:28 -0700786 for (idx, field) in fields.unnamed.iter().enumerate() {
787 let name = format!("_binding_{}", idx);
788 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400789
David Tolnay6af48992018-08-01 11:16:28 -0700790 bind_visit_fields.append_all(quote! {
791 ref #binding,
792 });
793 bind_visit_mut_fields.append_all(quote! {
794 ref mut #binding,
795 });
796 bind_fold_fields.append_all(quote! {
797 #binding,
798 });
Nika Layzell27726662017-10-24 23:16:35 -0400799
David Tolnay6af48992018-08-01 11:16:28 -0700800 let borrowed_binding = Borrowed(quote!(#binding));
801 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400802
David Tolnay6af48992018-08-01 11:16:28 -0700803 visit_fields.append_all(
804 visit(&field.ty, lookup, Visit, &borrowed_binding)
805 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
806 );
807 visit_mut_fields.append_all(
808 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
809 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
810 );
811 fold_fields.append_all(
812 visit(&field.ty, lookup, Fold, &owned_binding)
813 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
814 );
Nika Layzell27726662017-10-24 23:16:35 -0400815
David Tolnay6af48992018-08-01 11:16:28 -0700816 visit_fields.append_all(quote!(;));
817 visit_mut_fields.append_all(quote!(;));
818 fold_fields.append_all(quote!(,));
819 }
Nika Layzell27726662017-10-24 23:16:35 -0400820
David Tolnay6af48992018-08-01 11:16:28 -0700821 visit_variants.append_all(quote! {
822 #ty::#variant_ident(#bind_visit_fields) => {
823 #visit_fields
824 }
825 });
826
827 visit_mut_variants.append_all(quote! {
828 #ty::#variant_ident(#bind_visit_mut_fields) => {
829 #visit_mut_fields
830 }
831 });
832
833 fold_variants.append_all(quote! {
834 #ty::#variant_ident(#bind_fold_fields) => {
835 #ty::#variant_ident(
836 #fold_fields
837 )
838 }
839 });
Nika Layzell27726662017-10-24 23:16:35 -0400840 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500841 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700842 visit_variants.append_all(quote! {
843 #ty::#variant_ident => {}
844 });
845 visit_mut_variants.append_all(quote! {
846 #ty::#variant_ident => {}
847 });
848 fold_variants.append_all(quote! {
849 #ty::#variant_ident => {
850 #ty::#variant_ident
851 }
852 });
Nika Layzell27726662017-10-24 23:16:35 -0400853 }
Nika Layzell27726662017-10-24 23:16:35 -0400854 }
Nika Layzell27726662017-10-24 23:16:35 -0400855 }
David Tolnay6af48992018-08-01 11:16:28 -0700856
857 visit_impl.append_all(quote! {
858 match *_i {
859 #visit_variants
860 }
861 });
862
863 visit_mut_impl.append_all(quote! {
864 match *_i {
865 #visit_mut_variants
866 }
867 });
868
869 fold_impl.append_all(quote! {
870 match _i {
871 #fold_variants
872 }
873 });
Nika Layzell27726662017-10-24 23:16:35 -0400874 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500875 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700876 let mut fold_fields = TokenStream::new();
877
David Tolnaybc5b2c02018-08-02 00:18:23 -0700878 for (idx, field) in v.fields.iter().enumerate() {
879 let id = match field.ident {
880 Some(ref ident) => Member::Named(ident.clone()),
881 None => Member::Unnamed(Index::from(idx)),
882 };
883 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700884 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
885 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
886 visit_impl.append_all(quote! {
887 #visit_field;
888 });
889 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
890 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
891 visit_mut_impl.append_all(quote! {
892 #visit_mut_field;
893 });
David Tolnay83db9272017-12-28 17:02:31 -0500894 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700895 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400896 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700897 fold_fields.append_all(quote! {
898 #name: #fold,
899 });
Nika Layzell27726662017-10-24 23:16:35 -0400900 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700901 fold_fields.append_all(quote! {
902 #fold,
903 });
Nika Layzell27726662017-10-24 23:16:35 -0400904 }
905 }
906
David Tolnaye3d41b72017-12-31 15:24:00 -0500907 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700908 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
909 #ty {
910 #fold_fields
911 }
912 }),
913 Fields::Unit => {
914 if ty == "Ident" {
915 fold_impl.append_all(quote! {
916 let mut _i = _i;
917 let span = _visitor.fold_span(_i.span());
918 _i.set_span(span);
919 });
920 }
921 fold_impl.append_all(quote! {
922 _i
923 });
924 }
Nika Layzell27726662017-10-24 23:16:35 -0400925 };
926 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500927 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400928 }
929
David Tolnay6af48992018-08-01 11:16:28 -0700930 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800931 if let Data::Struct(ref data) = s.ast.data {
932 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700933 if fields
934 .named
935 .iter()
936 .any(|field| field.vis == Visibility::Inherited)
937 {
David Tolnay360efd22018-01-04 23:35:26 -0800938 // Discard the generated impl if there are private fields.
939 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700940 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800941 }
942 }
David Tolnayd0adf522017-12-29 01:30:07 -0500943 }
David Tolnay6af48992018-08-01 11:16:28 -0700944
945 state.visit_trait.append_all(quote! {
946 #features
947 fn #visit_fn(&mut self, i: &'ast #ty) {
948 #visit_fn(self, i)
949 }
950 });
951
952 state.visit_impl.append_all(quote! {
953 #features
954 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
955 _visitor: &mut V, _i: &'ast #ty
956 ) {
957 #visit_impl
958 }
959 });
960
961 state.visit_mut_trait.append_all(quote! {
962 #features
963 fn #visit_mut_fn(&mut self, i: &mut #ty) {
964 #visit_mut_fn(self, i)
965 }
966 });
967
968 state.visit_mut_impl.append_all(quote! {
969 #features
970 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
971 _visitor: &mut V, _i: &mut #ty
972 ) {
973 #visit_mut_impl
974 }
975 });
976
977 state.fold_trait.append_all(quote! {
978 #features
979 fn #fold_fn(&mut self, i: #ty) -> #ty {
980 #fold_fn(self, i)
981 }
982 });
983
984 if include_fold_impl {
985 state.fold_impl.append_all(quote! {
986 #features
987 pub fn #fold_fn<V: Fold + ?Sized>(
988 _visitor: &mut V, _i: #ty
989 ) -> #ty {
990 #fold_impl
991 }
992 });
993 }
Nika Layzell27726662017-10-24 23:16:35 -0400994 }
995}
996
David Tolnay6af48992018-08-01 11:16:28 -0700997fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700998 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -0700999 write!(
1000 file,
1001 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
1002 ).unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001003 let mut config = rustfmt::Config::default();
1004 config.set().emit_mode(rustfmt::EmitMode::Stdout);
1005 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -07001006 config.set().format_macro_matchers(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001007 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001008 session
1009 .format(rustfmt::Input::Text(content.to_string()))
1010 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001011}
1012
Nika Layzell27726662017-10-24 23:16:35 -04001013fn main() {
1014 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001015 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001016
Nika Layzellefb83ba2017-12-19 18:23:55 -05001017 // Load in any terminal types
1018 for &tt in TERMINAL_TYPES {
1019 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001020 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001021 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001022 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001023 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001024 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001025 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001026 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001027 }),
1028 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001029 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001030 data: Data::Struct(DataStruct {
1031 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001032 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001033 semi_token: None,
1034 }),
1035 },
hcplaa511792018-05-29 07:13:01 +03001036 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001037 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001038 },
David Tolnayd67fb752017-12-27 13:50:29 -05001039 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001040 }
1041
David Tolnay6af48992018-08-01 11:16:28 -07001042 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001043 for s in lookup.values() {
1044 codegen::generate(&mut state, &lookup, s);
1045 }
1046
David Tolnay6af48992018-08-01 11:16:28 -07001047 let full_macro = quote! {
1048 #[cfg(feature = "full")]
1049 macro_rules! full {
1050 ($e:expr) => {
1051 $e
1052 };
1053 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001054
David Tolnay6af48992018-08-01 11:16:28 -07001055 #[cfg(all(feature = "derive", not(feature = "full")))]
1056 macro_rules! full {
1057 ($e:expr) => {
1058 unreachable!()
1059 };
1060 }
1061 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001062
David Tolnay6af48992018-08-01 11:16:28 -07001063 let skip_macro = quote! {
1064 #[cfg(any(feature = "full", feature = "derive"))]
1065 macro_rules! skip {
1066 ($($tt:tt)*) => {};
1067 }
1068 };
1069
1070 let fold_trait = state.fold_trait;
1071 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001072 write_file(
1073 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001074 quote! {
1075 // Unreachable code is generated sometimes without the full feature.
1076 #![allow(unreachable_code)]
1077 #![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -04001078
David Tolnay6af48992018-08-01 11:16:28 -07001079 use *;
1080 #[cfg(any(feature = "full", feature = "derive"))]
1081 use token::{Brace, Bracket, Paren, Group};
1082 use proc_macro2::Span;
1083 #[cfg(any(feature = "full", feature = "derive"))]
1084 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001085
David Tolnay6af48992018-08-01 11:16:28 -07001086 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001087
David Tolnay6af48992018-08-01 11:16:28 -07001088 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1089 ///
1090 /// See the [module documentation] for details.
1091 ///
1092 /// [module documentation]: index.html
1093 ///
1094 /// *This trait is available if Syn is built with the `"fold"` feature.*
1095 pub trait Fold {
1096 #fold_trait
1097 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001098
David Tolnay6af48992018-08-01 11:16:28 -07001099 #[cfg(any(feature = "full", feature = "derive"))]
1100 macro_rules! fold_span_only {
1101 ($f:ident : $t:ident) => {
1102 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1103 let span = _visitor.fold_span(_i.span());
1104 _i.set_span(span);
1105 _i
1106 }
1107 }
1108 }
Nika Layzell27726662017-10-24 23:16:35 -04001109
David Tolnay6af48992018-08-01 11:16:28 -07001110 #[cfg(any(feature = "full", feature = "derive"))]
1111 fold_span_only!(fold_lit_byte: LitByte);
1112 #[cfg(any(feature = "full", feature = "derive"))]
1113 fold_span_only!(fold_lit_byte_str: LitByteStr);
1114 #[cfg(any(feature = "full", feature = "derive"))]
1115 fold_span_only!(fold_lit_char: LitChar);
1116 #[cfg(any(feature = "full", feature = "derive"))]
1117 fold_span_only!(fold_lit_float: LitFloat);
1118 #[cfg(any(feature = "full", feature = "derive"))]
1119 fold_span_only!(fold_lit_int: LitInt);
1120 #[cfg(any(feature = "full", feature = "derive"))]
1121 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001122
David Tolnay6af48992018-08-01 11:16:28 -07001123 #fold_impl
1124 },
David Tolnayae0009e2018-08-01 00:40:00 -07001125 );
Nika Layzell27726662017-10-24 23:16:35 -04001126
David Tolnay6af48992018-08-01 11:16:28 -07001127 let visit_trait = state.visit_trait;
1128 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001129 write_file(
1130 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001131 quote! {
1132 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001133
David Tolnay6af48992018-08-01 11:16:28 -07001134 use *;
1135 #[cfg(any(feature = "full", feature = "derive"))]
1136 use punctuated::Punctuated;
1137 use proc_macro2::Span;
1138 #[cfg(any(feature = "full", feature = "derive"))]
1139 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001140
David Tolnay6af48992018-08-01 11:16:28 -07001141 #full_macro
1142 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001143
David Tolnay6af48992018-08-01 11:16:28 -07001144 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1145 ///
1146 /// See the [module documentation] for details.
1147 ///
1148 /// [module documentation]: index.html
1149 ///
1150 /// *This trait is available if Syn is built with the `"visit"` feature.*
1151 pub trait Visit<'ast> {
1152 #visit_trait
1153 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001154
David Tolnay6af48992018-08-01 11:16:28 -07001155 #visit_impl
1156 },
David Tolnayae0009e2018-08-01 00:40:00 -07001157 );
Nika Layzell27726662017-10-24 23:16:35 -04001158
David Tolnay6af48992018-08-01 11:16:28 -07001159 let visit_mut_trait = state.visit_mut_trait;
1160 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001161 write_file(
1162 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001163 quote! {
1164 #![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Nika Layzell27726662017-10-24 23:16:35 -04001165
David Tolnay6af48992018-08-01 11:16:28 -07001166 use *;
1167 #[cfg(any(feature = "full", feature = "derive"))]
1168 use punctuated::Punctuated;
1169 use proc_macro2::Span;
1170 #[cfg(any(feature = "full", feature = "derive"))]
1171 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001172
David Tolnay6af48992018-08-01 11:16:28 -07001173 #full_macro
1174 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001175
David Tolnay6af48992018-08-01 11:16:28 -07001176 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1177 /// place.
1178 ///
1179 /// See the [module documentation] for details.
1180 ///
1181 /// [module documentation]: index.html
1182 ///
1183 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1184 pub trait VisitMut {
1185 #visit_mut_trait
1186 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001187
David Tolnay6af48992018-08-01 11:16:28 -07001188 #visit_mut_impl
1189 },
David Tolnayae0009e2018-08-01 00:40:00 -07001190 );
Nika Layzell27726662017-10-24 23:16:35 -04001191}