blob: ed600ace7dc824c376b0135b1b3b4dd5e1f206c5 [file] [log] [blame]
David Tolnay4b4c4b62018-01-06 13:48:05 -08001//! This crate automatically generates the definition of the `Visit`,
2//! `VisitMut`, and `Fold` traits in `syn` based on the `syn` source. It
Nika Layzell27726662017-10-24 23:16:35 -04003//! discovers structs and enums declared with the `ast_*` macros and generates
4//! the functions for those types.
5//!
6//! It makes a few assumptions about the target crate:
7//! 1. All structs which are discovered must be re-exported in the root of the
8//! crate, even if they were declared in a submodule.
9//! 2. This code cannot discover submodules which are located in subdirectories
10//! - only submodules located in the same directory.
11//! 3. The path to `syn` is hardcoded.
12
David Tolnay6af48992018-08-01 11:16:28 -070013#![recursion_limit = "128"]
David Tolnayc1f55792018-11-21 01:39:42 -080014#![allow(clippy::needless_pass_by_value)]
David Tolnayea9ae892017-12-26 01:44:32 -050015
David Tolnayd67fb752017-12-27 13:50:29 -050016#[macro_use]
17extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040018extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050019extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050020#[macro_use]
21extern crate quote;
David Tolnay8c81f622018-07-31 23:34:35 -070022extern crate rustfmt_nightly as rustfmt;
David Tolnay5f794802018-11-24 14:51:21 -080023extern crate syn;
Nika Layzell27726662017-10-24 23:16:35 -040024
David Tolnayd67fb752017-12-27 13:50:29 -050025use failure::{err_msg, Error};
David Tolnaye303b7c2018-05-20 16:46:35 -070026use proc_macro2::{Span, TokenStream};
David Tolnay01ed0ce2018-05-20 20:18:14 -070027use quote::ToTokens;
28use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
Nika Layzell27726662017-10-24 23:16:35 -040029
David Tolnay01ed0ce2018-05-20 20:18:14 -070030use std::collections::BTreeMap;
David Tolnayf0d63bf2017-12-26 12:29:47 -050031use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040032use std::fs::File;
David Tolnay6af48992018-08-01 11:16:28 -070033use std::io::{Read, Write};
Nika Layzell27726662017-10-24 23:16:35 -040034use std::path::Path;
Nika Layzell27726662017-10-24 23:16:35 -040035
36const SYN_CRATE_ROOT: &str = "../src/lib.rs";
37
38const FOLD_SRC: &str = "../src/gen/fold.rs";
39const VISIT_SRC: &str = "../src/gen/visit.rs";
40const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
41
David Tolnayd67fb752017-12-27 13:50:29 -050042const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040043
Alex Crichton131308c2018-05-18 14:00:24 -070044const EXTRA_TYPES: &[&str] = &["Lifetime"];
David Tolnay4ba63a02017-12-28 15:53:05 -050045
Alex Crichtond261d092018-05-18 13:47:35 -070046const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050047
Alex Crichton715862b2018-05-17 12:31:49 -070048fn get_features(attrs: &[Attribute], mut features: TokenStream) -> TokenStream {
Nika Layzell27726662017-10-24 23:16:35 -040049 for attr in attrs {
David Tolnaycdf76eb2018-11-24 14:54:33 -080050 if attr.path.is_ident("cfg") {
Nika Layzell27726662017-10-24 23:16:35 -040051 attr.to_tokens(&mut features);
52 }
53 }
54 features
55}
56
57#[derive(Clone)]
58pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050059 ast: DeriveInput,
Alex Crichton715862b2018-05-17 12:31:49 -070060 features: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -040061 // True if this is an ast_enum_of_structs! item with a #full annotation.
62 eos_full: bool,
63}
64
David Tolnayf0d63bf2017-12-26 12:29:47 -050065impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040066 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050068 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040069 .field("features", &self.features.to_string())
70 .finish()
71 }
72}
73
74// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050075type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040076
David Tolnay01ed0ce2018-05-20 20:18:14 -070077fn load_file<P: AsRef<Path>>(
78 name: P,
79 features: &TokenStream,
80 lookup: &mut Lookup,
81) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040082 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050083 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040084
85 let mut f = File::open(name)?;
86 let mut src = String::new();
87 f.read_to_string(&mut src)?;
88
89 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050090 let file =
91 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -040092
93 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -050094 'items: for item in file.items {
95 match item {
96 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -040097 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -080098 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -040099 continue;
100 }
101
102 // We don't want to try to load the generated rust files and
103 // parse them, so we ignore them here.
104 for name in IGNORED_MODS {
David Tolnay446f7d62018-05-20 17:58:15 -0700105 if item.ident == name {
Nika Layzell27726662017-10-24 23:16:35 -0400106 continue 'items;
107 }
108 }
109
110 // Lookup any #[cfg()] attributes on the module and add them to
111 // the feature set.
David Tolnay0a0d78c2018-01-05 15:24:01 -0800112 //
113 // The derive module is weird because it is built with either
114 // `full` or `derive` but exported only under `derive`.
David Tolnay446f7d62018-05-20 17:58:15 -0700115 let features = if item.ident == "derive" {
David Tolnay0a0d78c2018-01-05 15:24:01 -0800116 quote!(#[cfg(feature = "derive")])
117 } else {
118 get_features(&item.attrs, features.clone())
119 };
Nika Layzell27726662017-10-24 23:16:35 -0400120
121 // Look up the submodule file, and recursively parse it.
122 // XXX: Only handles same-directory .rs file submodules.
Alex Crichtona74a1c82018-05-16 10:20:44 -0700123 let path = parent.join(&format!("{}.rs", item.ident));
David Tolnayea9ae892017-12-26 01:44:32 -0500124 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400125 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500126 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400127 // Lookip any #[cfg()] attributes directly on the macro
128 // invocation, and add them to the feature set.
129 let features = get_features(&item.attrs, features.clone());
130
131 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800132 let tts = &item.mac.tts;
David Tolnaycdf76eb2018-11-24 14:54:33 -0800133 let found = if item.mac.path.is_ident("ast_struct") {
David Tolnay64f03842018-08-30 21:34:40 -0700134 syn::parse2::<parsing::AstStruct>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500135 .map_err(|_| err_msg("failed to parse ast_struct"))?
136 .0
David Tolnaycdf76eb2018-11-24 14:54:33 -0800137 } else if item.mac.path.is_ident("ast_enum") {
David Tolnay64f03842018-08-30 21:34:40 -0700138 syn::parse2::<parsing::AstEnum>(quote!(#tts))
David Tolnayd67fb752017-12-27 13:50:29 -0500139 .map_err(|_| err_msg("failed to parse ast_enum"))?
140 .0
David Tolnaycdf76eb2018-11-24 14:54:33 -0800141 } else if item.mac.path.is_ident("ast_enum_of_structs") {
David Tolnay64f03842018-08-30 21:34:40 -0700142 syn::parse2::<parsing::AstEnumOfStructs>(quote!(#tts))
David Tolnay1cf80912017-12-31 18:35:12 -0500143 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500144 .0
Nika Layzell27726662017-10-24 23:16:35 -0400145 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500146 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400147 };
148
149 // Record our features on the parsed AstItems.
150 for mut item in found {
151 features.to_tokens(&mut item.features);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700152 lookup.insert(item.ast.ident.clone(), item);
Nika Layzell27726662017-10-24 23:16:35 -0400153 }
154 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500155 Item::Struct(item) => {
156 let ident = item.ident;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700157 if EXTRA_TYPES.contains(&&ident.to_string()[..]) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700158 lookup.insert(
159 ident.clone(),
160 AstItem {
161 ast: DeriveInput {
David Tolnay6b46a702018-08-01 23:51:32 -0700162 ident,
David Tolnay01ed0ce2018-05-20 20:18:14 -0700163 vis: item.vis,
164 attrs: item.attrs,
165 generics: item.generics,
166 data: Data::Struct(DataStruct {
167 fields: item.fields,
168 struct_token: item.struct_token,
169 semi_token: item.semi_token,
170 }),
171 },
172 features: features.clone(),
173 eos_full: false,
David Tolnay4ba63a02017-12-28 15:53:05 -0500174 },
David Tolnay01ed0ce2018-05-20 20:18:14 -0700175 );
David Tolnay4ba63a02017-12-28 15:53:05 -0500176 }
177 }
Nika Layzell27726662017-10-24 23:16:35 -0400178 _ => {}
179 }
180 }
181 Ok(())
182}
183
184mod parsing {
185 use super::AstItem;
186
David Tolnay01ed0ce2018-05-20 20:18:14 -0700187 use proc_macro2::TokenStream;
David Tolnay1cf80912017-12-31 18:35:12 -0500188 use syn;
David Tolnay64f03842018-08-30 21:34:40 -0700189 use syn::parse::{Parse, ParseStream, Result};
Nika Layzell27726662017-10-24 23:16:35 -0400190 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400191
David Tolnay64f03842018-08-30 21:34:40 -0700192 fn peek_tag(input: ParseStream, tag: &str) -> bool {
193 let ahead = input.fork();
David Tolnay5f794802018-11-24 14:51:21 -0800194 ahead.parse::<Token![#]>().is_ok()
195 && ahead
196 .parse::<Ident>()
197 .map(|ident| ident == tag)
198 .unwrap_or(false)
David Tolnay64f03842018-08-30 21:34:40 -0700199 }
200
Nika Layzell27726662017-10-24 23:16:35 -0400201 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
202 // nothing otherwise.
David Tolnay64f03842018-08-30 21:34:40 -0700203 fn full(input: ParseStream) -> (TokenStream, bool) {
204 if peek_tag(input, "full") {
205 input.parse::<Token![#]>().unwrap();
206 input.parse::<Ident>().unwrap();
207 (quote!(#[cfg(feature = "full")]), true)
208 } else {
209 (quote!(), false)
210 }
211 }
Nika Layzell27726662017-10-24 23:16:35 -0400212
David Tolnay64f03842018-08-30 21:34:40 -0700213 fn skip_manual_extra_traits(input: ParseStream) {
214 if peek_tag(input, "manual_extra_traits") {
215 input.parse::<Token![#]>().unwrap();
216 input.parse::<Ident>().unwrap();
217 }
218 }
David Tolnay28c5a462017-12-27 01:59:30 -0500219
Nika Layzell27726662017-10-24 23:16:35 -0400220 // Parses a simple AstStruct without the `pub struct` prefix.
David Tolnay64f03842018-08-30 21:34:40 -0700221 fn ast_struct_inner(input: ParseStream) -> Result<AstItem> {
222 let ident: Ident = input.parse()?;
223 let (features, eos_full) = full(input);
224 skip_manual_extra_traits(input);
225 let rest: TokenStream = input.parse()?;
226 Ok(AstItem {
227 ast: syn::parse2(quote! {
228 pub struct #ident #rest
229 })?,
David Tolnay9a518142018-11-21 01:38:50 -0800230 features,
231 eos_full,
Nika Layzell27726662017-10-24 23:16:35 -0400232 })
David Tolnay64f03842018-08-30 21:34:40 -0700233 }
Nika Layzell27726662017-10-24 23:16:35 -0400234
235 // ast_struct! parsing
236 pub struct AstStruct(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700237 impl Parse for AstStruct {
238 fn parse(input: ParseStream) -> Result<Self> {
239 input.call(Attribute::parse_outer)?;
240 input.parse::<Token![pub]>()?;
241 input.parse::<Token![struct]>()?;
242 let res = input.call(ast_struct_inner)?;
243 Ok(AstStruct(vec![res]))
244 }
Nika Layzell27726662017-10-24 23:16:35 -0400245 }
246
David Tolnay64f03842018-08-30 21:34:40 -0700247 fn no_visit(input: ParseStream) -> bool {
248 if peek_tag(input, "no_visit") {
249 input.parse::<Token![#]>().unwrap();
250 input.parse::<Ident>().unwrap();
251 true
252 } else {
253 false
254 }
255 }
David Tolnay360efd22018-01-04 23:35:26 -0800256
Nika Layzell27726662017-10-24 23:16:35 -0400257 // ast_enum! parsing
258 pub struct AstEnum(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700259 impl Parse for AstEnum {
260 fn parse(input: ParseStream) -> Result<Self> {
261 input.call(Attribute::parse_outer)?;
262 input.parse::<Token![pub]>()?;
263 input.parse::<Token![enum]>()?;
264 let ident: Ident = input.parse()?;
265 let no_visit = no_visit(input);
266 let rest: TokenStream = input.parse()?;
267 Ok(AstEnum(if no_visit {
David Tolnay360efd22018-01-04 23:35:26 -0800268 vec![]
269 } else {
270 vec![AstItem {
David Tolnay64f03842018-08-30 21:34:40 -0700271 ast: syn::parse2(quote! {
272 pub enum #ident #rest
273 })?,
David Tolnay360efd22018-01-04 23:35:26 -0800274 features: quote!(),
275 eos_full: false,
276 }]
277 }))
David Tolnay64f03842018-08-30 21:34:40 -0700278 }
Nika Layzell27726662017-10-24 23:16:35 -0400279 }
280
281 // A single variant of an ast_enum_of_structs!
282 struct EosVariant {
283 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500284 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400285 inner: Option<AstItem>,
286 }
David Tolnay64f03842018-08-30 21:34:40 -0700287 fn eos_variant(input: ParseStream) -> Result<EosVariant> {
288 input.call(Attribute::parse_outer)?;
289 input.parse::<Token![pub]>()?;
290 let variant: Ident = input.parse()?;
291 let (member, inner) = if input.peek(token::Paren) {
292 let content;
293 parenthesized!(content in input);
294 if content.fork().call(ast_struct_inner).is_ok() {
295 let item = content.call(ast_struct_inner)?;
296 (Some(Path::from(item.ast.ident.clone())), Some(item))
297 } else {
298 let path: Path = content.parse()?;
299 (Some(path), None)
300 }
301 } else {
302 (None, None)
303 };
304 input.parse::<Token![,]>()?;
305 Ok(EosVariant {
Nika Layzell27726662017-10-24 23:16:35 -0400306 name: variant,
David Tolnay9a518142018-11-21 01:38:50 -0800307 member,
308 inner,
Nika Layzell27726662017-10-24 23:16:35 -0400309 })
David Tolnay64f03842018-08-30 21:34:40 -0700310 }
Nika Layzell27726662017-10-24 23:16:35 -0400311
312 // ast_enum_of_structs! parsing
313 pub struct AstEnumOfStructs(pub Vec<AstItem>);
David Tolnay64f03842018-08-30 21:34:40 -0700314 impl Parse for AstEnumOfStructs {
315 fn parse(input: ParseStream) -> Result<Self> {
316 input.call(Attribute::parse_outer)?;
317 input.parse::<Token![pub]>()?;
318 input.parse::<Token![enum]>()?;
319 let ident: Ident = input.parse()?;
320
321 let content;
322 braced!(content in input);
323 let mut variants = Vec::new();
324 while !content.is_empty() {
325 variants.push(content.call(eos_variant)?);
326 }
327
328 if let Some(ident) = input.parse::<Option<Ident>>()? {
329 assert_eq!(ident, "do_not_generate_to_tokens");
330 }
331
332 let enum_item = {
333 let variants = variants.iter().map(|v| {
334 let name = v.name.clone();
335 match v.member {
336 Some(ref member) => quote!(#name(#member)),
337 None => quote!(#name),
David Tolnayb7ccc4f2018-08-02 00:41:32 -0700338 }
David Tolnay64f03842018-08-30 21:34:40 -0700339 });
340 parse_quote! {
341 pub enum #ident {
342 #(#variants),*
343 }
344 }
345 };
346 let mut items = vec![AstItem {
347 ast: enum_item,
348 features: quote!(),
David Tolnay86cb9452018-08-31 10:35:09 -0700349 eos_full: false,
David Tolnay64f03842018-08-30 21:34:40 -0700350 }];
351 items.extend(variants.into_iter().filter_map(|v| v.inner));
352 Ok(AstEnumOfStructs(items))
353 }
Nika Layzell27726662017-10-24 23:16:35 -0400354 }
355}
356
357mod codegen {
358 use super::{AstItem, Lookup};
David Tolnay6b46a702018-08-01 23:51:32 -0700359 use inflections::Inflect;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700360 use proc_macro2::{Span, TokenStream};
David Tolnay6af48992018-08-01 11:16:28 -0700361 use quote::{ToTokens, TokenStreamExt};
David Tolnay86cb9452018-08-31 10:35:09 -0700362 use syn::ext::IdentExt;
363 use syn::parse::Parser;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700364 use syn::punctuated::Punctuated;
365 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400366
367 #[derive(Default)]
368 pub struct State {
David Tolnay6af48992018-08-01 11:16:28 -0700369 pub visit_trait: TokenStream,
370 pub visit_impl: TokenStream,
371 pub visit_mut_trait: TokenStream,
372 pub visit_mut_impl: TokenStream,
373 pub fold_trait: TokenStream,
374 pub fold_impl: TokenStream,
Nika Layzell27726662017-10-24 23:16:35 -0400375 }
376
David Tolnay4a918742017-12-28 16:54:41 -0500377 fn under_name(name: Ident) -> Ident {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700378 Ident::new(&name.to_string().to_snake_case(), Span::call_site())
Nika Layzell27726662017-10-24 23:16:35 -0400379 }
380
David Tolnay3d772182017-12-28 17:18:53 -0500381 enum RelevantType<'a> {
382 Box(&'a Type),
383 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500384 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500385 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500386 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500387 Simple(&'a AstItem),
David Tolnay7ac699c2018-08-24 14:00:58 -0400388 TokenPunct(TokenStream),
389 TokenKeyword(TokenStream),
390 TokenGroup(Ident),
David Tolnay3d772182017-12-28 17:18:53 -0500391 Pass,
392 }
Nika Layzell27726662017-10-24 23:16:35 -0400393
David Tolnay3d772182017-12-28 17:18:53 -0500394 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
395 match *ty {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700396 Type::Path(TypePath {
397 qself: None,
398 ref path,
399 }) => {
David Tolnay56080682018-01-06 14:01:52 -0800400 let last = path.segments.last().unwrap().into_value();
Alex Crichtona74a1c82018-05-16 10:20:44 -0700401 match &last.ident.to_string()[..] {
David Tolnay3d772182017-12-28 17:18:53 -0500402 "Box" => RelevantType::Box(first_arg(&last.arguments)),
403 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500404 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500405 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500406 "Brace" | "Bracket" | "Paren" | "Group" => {
David Tolnay7ac699c2018-08-24 14:00:58 -0400407 RelevantType::TokenGroup(last.ident.clone())
David Tolnay1e01f9c2017-12-28 20:16:19 -0500408 }
David Tolnay3d772182017-12-28 17:18:53 -0500409 _ => {
410 if let Some(item) = lookup.get(&last.ident) {
411 RelevantType::Simple(item)
412 } else {
413 RelevantType::Pass
414 }
415 }
416 }
Nika Layzell27726662017-10-24 23:16:35 -0400417 }
David Tolnay01ed0ce2018-05-20 20:18:14 -0700418 Type::Tuple(TypeTuple { ref elems, .. }) => RelevantType::Tuple(elems),
419 Type::Macro(TypeMacro { ref mac })
420 if mac.path.segments.last().unwrap().into_value().ident == "Token" =>
421 {
David Tolnay86cb9452018-08-31 10:35:09 -0700422 let is_ident = Ident::parse_any.parse2(mac.tts.clone()).is_ok();
David Tolnay7ac699c2018-08-24 14:00:58 -0400423 let is_underscore = parse2::<Token![_]>(mac.tts.clone()).is_ok();
424 if is_ident && !is_underscore {
425 RelevantType::TokenKeyword(mac.into_token_stream())
426 } else {
427 RelevantType::TokenPunct(mac.into_token_stream())
428 }
David Tolnaycc0f0372017-12-28 19:11:04 -0500429 }
David Tolnay3d772182017-12-28 17:18:53 -0500430 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400431 }
432 }
433
434 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
435 enum Kind {
436 Visit,
437 VisitMut,
438 Fold,
439 }
440
David Tolnayf0d63bf2017-12-26 12:29:47 -0500441 enum Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700442 Borrowed(TokenStream),
443 Owned(TokenStream),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500444 }
445
David Tolnay83db9272017-12-28 17:02:31 -0500446 use self::Kind::*;
David Tolnay01ed0ce2018-05-20 20:18:14 -0700447 use self::Operand::*;
David Tolnay83db9272017-12-28 17:02:31 -0500448
David Tolnayf0d63bf2017-12-26 12:29:47 -0500449 impl Operand {
Alex Crichton715862b2018-05-17 12:31:49 -0700450 fn tokens(&self) -> &TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500451 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500452 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500453 }
454 }
455
Alex Crichton715862b2018-05-17 12:31:49 -0700456 fn ref_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500457 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500458 Borrowed(ref n) => n.clone(),
459 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500460 }
461 }
462
Alex Crichton715862b2018-05-17 12:31:49 -0700463 fn ref_mut_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500464 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500465 Borrowed(ref n) => n.clone(),
466 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500467 }
468 }
469
Alex Crichton715862b2018-05-17 12:31:49 -0700470 fn owned_tokens(&self) -> TokenStream {
David Tolnayf0d63bf2017-12-26 12:29:47 -0500471 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500472 Borrowed(ref n) => quote!(*#n),
473 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500474 }
475 }
476 }
477
Nika Layzellc08227a2017-12-04 16:30:17 -0500478 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400479 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500480 PathArguments::AngleBracketed(ref data) => data,
481 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400482 };
483
David Tolnay01ed0ce2018-05-20 20:18:14 -0700484 match **data
485 .args
David Tolnayd67fb752017-12-27 13:50:29 -0500486 .first()
487 .expect("Expected at least 1 type argument here")
David Tolnay56080682018-01-06 14:01:52 -0800488 .value()
David Tolnayd67fb752017-12-27 13:50:29 -0500489 {
David Tolnayea9ae892017-12-26 01:44:32 -0500490 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500491 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500492 }
Nika Layzell27726662017-10-24 23:16:35 -0400493 }
494
David Tolnay6af48992018-08-01 11:16:28 -0700495 fn simple_visit(item: &AstItem, kind: Kind, name: &Operand) -> TokenStream {
496 let ident = under_name(item.ast.ident.clone());
497
David Tolnay4a918742017-12-28 16:54:41 -0500498 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700499 Visit => {
500 let method = Ident::new(&format!("visit_{}", ident), Span::call_site());
501 let name = name.ref_tokens();
502 quote! {
503 _visitor.#method(#name)
504 }
505 }
506 VisitMut => {
507 let method = Ident::new(&format!("visit_{}_mut", ident), Span::call_site());
508 let name = name.ref_mut_tokens();
509 quote! {
510 _visitor.#method(#name)
511 }
512 }
513 Fold => {
514 let method = Ident::new(&format!("fold_{}", ident), Span::call_site());
515 let name = name.owned_tokens();
516 quote! {
517 _visitor.#method(#name)
518 }
519 }
Nika Layzell27726662017-10-24 23:16:35 -0400520 }
521 }
522
David Tolnay6af48992018-08-01 11:16:28 -0700523 fn box_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500524 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500525 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500526 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700527 Fold => quote! {
528 Box::new(#res)
529 },
David Tolnay83db9272017-12-28 17:02:31 -0500530 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500531 })
Nika Layzell27726662017-10-24 23:16:35 -0400532 }
533
David Tolnay6af48992018-08-01 11:16:28 -0700534 fn vec_visit(elem: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500535 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500536 Visit | VisitMut => Borrowed(quote!(it)),
537 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500538 };
David Tolnay39d0a202017-12-28 18:19:00 -0500539 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500540 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700541 Visit => {
542 let name = name.ref_tokens();
543 quote! {
544 for it in #name {
545 #val
546 }
547 }
548 }
549 VisitMut => {
550 let name = name.ref_mut_tokens();
551 quote! {
552 for it in #name {
553 #val
554 }
555 }
556 }
557 Fold => {
558 let name = name.owned_tokens();
559 quote! {
560 FoldHelper::lift(#name, |it| { #val })
561 }
562 }
David Tolnay3d772182017-12-28 17:18:53 -0500563 })
564 }
565
David Tolnay6eff4da2018-01-01 20:27:45 -0800566 fn punctuated_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500567 elem: &Type,
568 lookup: &Lookup,
569 kind: Kind,
570 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700571 ) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500572 let operand = match kind {
573 Visit | VisitMut => Borrowed(quote!(it)),
574 Fold => Owned(quote!(it)),
575 };
David Tolnay39d0a202017-12-28 18:19:00 -0500576 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500577 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700578 Visit => {
579 let name = name.ref_tokens();
580 quote! {
581 for el in Punctuated::pairs(#name) {
582 let it = el.value();
583 #val
584 }
585 }
586 }
587 VisitMut => {
588 let name = name.ref_mut_tokens();
589 quote! {
590 for mut el in Punctuated::pairs_mut(#name) {
591 let it = el.value_mut();
592 #val
593 }
594 }
595 }
596 Fold => {
597 let name = name.owned_tokens();
598 quote! {
599 FoldHelper::lift(#name, |it| { #val })
600 }
601 }
David Tolnay4a918742017-12-28 16:54:41 -0500602 })
Nika Layzell27726662017-10-24 23:16:35 -0400603 }
604
David Tolnay6af48992018-08-01 11:16:28 -0700605 fn option_visit(
606 elem: &Type,
607 lookup: &Lookup,
608 kind: Kind,
609 name: &Operand,
610 ) -> Option<TokenStream> {
David Tolnay4a918742017-12-28 16:54:41 -0500611 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500612 Visit | VisitMut => Borrowed(quote!(it)),
613 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500614 };
David Tolnay39d0a202017-12-28 18:19:00 -0500615 let val = visit(elem, lookup, kind, &it)?;
David Tolnay6af48992018-08-01 11:16:28 -0700616 let name = name.owned_tokens();
David Tolnay4a918742017-12-28 16:54:41 -0500617 Some(match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700618 Visit => quote! {
619 if let Some(ref it) = #name {
620 #val
621 }
622 },
623 VisitMut => quote! {
624 if let Some(ref mut it) = #name {
625 #val
626 }
627 },
628 Fold => quote! {
629 (#name).map(|it| { #val })
630 },
David Tolnay4a918742017-12-28 16:54:41 -0500631 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400632 }
633
David Tolnay5c4c0b52017-12-28 17:58:54 -0500634 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500635 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500636 lookup: &Lookup,
637 kind: Kind,
638 name: &Operand,
David Tolnay6af48992018-08-01 11:16:28 -0700639 ) -> Option<TokenStream> {
640 if elems.is_empty() {
641 return None;
642 }
643
644 let mut code = TokenStream::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500645 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500646 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500647 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500648 let it = Owned(quote!((#name).#i));
David Tolnay01ed0ce2018-05-20 20:18:14 -0700649 let val = visit(elem, lookup, kind, &it).unwrap_or_else(|| noop_visit(kind, &it));
David Tolnay6af48992018-08-01 11:16:28 -0700650 code.append_all(val);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500651 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700652 Fold => code.append_all(quote!(,)),
653 Visit | VisitMut => code.append_all(quote!(;)),
David Tolnay5c4c0b52017-12-28 17:58:54 -0500654 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500655 }
David Tolnay6af48992018-08-01 11:16:28 -0700656 Some(match kind {
657 Fold => quote! {
658 (#code)
659 },
660 Visit | VisitMut => code,
661 })
David Tolnay5c4c0b52017-12-28 17:58:54 -0500662 }
663
David Tolnay7ac699c2018-08-24 14:00:58 -0400664 fn token_punct_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
David Tolnay6af48992018-08-01 11:16:28 -0700665 let name = name.tokens();
David Tolnaycc0f0372017-12-28 19:11:04 -0500666 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700667 Fold => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400668 #ty(tokens_helper(_visitor, &#name.spans))
David Tolnay6af48992018-08-01 11:16:28 -0700669 },
670 Visit => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400671 tokens_helper(_visitor, &#name.spans)
David Tolnay6af48992018-08-01 11:16:28 -0700672 },
673 VisitMut => quote! {
David Tolnay7ac699c2018-08-24 14:00:58 -0400674 tokens_helper(_visitor, &mut #name.spans)
675 },
676 }
677 }
678
679 fn token_keyword_visit(ty: TokenStream, kind: Kind, name: &Operand) -> TokenStream {
680 let name = name.tokens();
681 match kind {
682 Fold => quote! {
683 #ty(tokens_helper(_visitor, &#name.span))
684 },
685 Visit => quote! {
686 tokens_helper(_visitor, &#name.span)
687 },
688 VisitMut => quote! {
689 tokens_helper(_visitor, &mut #name.span)
690 },
691 }
692 }
693
694 fn token_group_visit(ty: Ident, kind: Kind, name: &Operand) -> TokenStream {
695 let name = name.tokens();
696 match kind {
697 Fold => quote! {
698 #ty(tokens_helper(_visitor, &#name.span))
699 },
700 Visit => quote! {
701 tokens_helper(_visitor, &#name.span)
702 },
703 VisitMut => quote! {
704 tokens_helper(_visitor, &mut #name.span)
David Tolnay6af48992018-08-01 11:16:28 -0700705 },
David Tolnaycc0f0372017-12-28 19:11:04 -0500706 }
707 }
708
David Tolnay6af48992018-08-01 11:16:28 -0700709 fn noop_visit(kind: Kind, name: &Operand) -> TokenStream {
David Tolnay4a918742017-12-28 16:54:41 -0500710 match kind {
David Tolnay6af48992018-08-01 11:16:28 -0700711 Fold => name.owned_tokens(),
712 Visit | VisitMut => {
713 let name = name.tokens();
714 quote! {
715 skip!(#name)
716 }
717 }
David Tolnay4a918742017-12-28 16:54:41 -0500718 }
719 }
720
David Tolnay6af48992018-08-01 11:16:28 -0700721 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<TokenStream> {
David Tolnay3d772182017-12-28 17:18:53 -0500722 match classify(ty, lookup) {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700723 RelevantType::Box(elem) => box_visit(elem, lookup, kind, name),
724 RelevantType::Vec(elem) => vec_visit(elem, lookup, kind, name),
725 RelevantType::Punctuated(elem) => punctuated_visit(elem, lookup, kind, name),
726 RelevantType::Option(elem) => option_visit(elem, lookup, kind, name),
727 RelevantType::Tuple(elems) => tuple_visit(elems, lookup, kind, name),
David Tolnay3d772182017-12-28 17:18:53 -0500728 RelevantType::Simple(item) => {
729 let mut res = simple_visit(item, kind, name);
730 Some(if item.eos_full {
David Tolnay6af48992018-08-01 11:16:28 -0700731 quote! {
732 full!(#res)
733 }
David Tolnay3d772182017-12-28 17:18:53 -0500734 } else {
735 res
736 })
737 }
David Tolnay7ac699c2018-08-24 14:00:58 -0400738 RelevantType::TokenPunct(ty) => Some(token_punct_visit(ty, kind, name)),
739 RelevantType::TokenKeyword(ty) => Some(token_keyword_visit(ty, kind, name)),
740 RelevantType::TokenGroup(ty) => Some(token_group_visit(ty, kind, name)),
David Tolnay01ed0ce2018-05-20 20:18:14 -0700741 RelevantType::Pass => None,
Nika Layzell27726662017-10-24 23:16:35 -0400742 }
Nika Layzell27726662017-10-24 23:16:35 -0400743 }
744
745 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay6af48992018-08-01 11:16:28 -0700746 let features = &s.features;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700747 let under_name = under_name(s.ast.ident.clone());
David Tolnay6af48992018-08-01 11:16:28 -0700748 let ty = &s.ast.ident;
749 let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
750 let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
751 let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400752
David Tolnay6af48992018-08-01 11:16:28 -0700753 let mut visit_impl = TokenStream::new();
754 let mut visit_mut_impl = TokenStream::new();
755 let mut fold_impl = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400756
David Tolnaye3d41b72017-12-31 15:24:00 -0500757 match s.ast.data {
758 Data::Enum(ref e) => {
David Tolnay6af48992018-08-01 11:16:28 -0700759 let mut visit_variants = TokenStream::new();
760 let mut visit_mut_variants = TokenStream::new();
761 let mut fold_variants = TokenStream::new();
762
Nika Layzell27726662017-10-24 23:16:35 -0400763 for variant in &e.variants {
David Tolnay6af48992018-08-01 11:16:28 -0700764 let variant_ident = &variant.ident;
765
766 match variant.fields {
David Tolnaye3d41b72017-12-31 15:24:00 -0500767 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
768 Fields::Unnamed(ref fields) => {
David Tolnay6af48992018-08-01 11:16:28 -0700769 let mut bind_visit_fields = TokenStream::new();
770 let mut bind_visit_mut_fields = TokenStream::new();
771 let mut bind_fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400772
David Tolnay6af48992018-08-01 11:16:28 -0700773 let mut visit_fields = TokenStream::new();
774 let mut visit_mut_fields = TokenStream::new();
775 let mut fold_fields = TokenStream::new();
Nika Layzell27726662017-10-24 23:16:35 -0400776
David Tolnay6af48992018-08-01 11:16:28 -0700777 for (idx, field) in fields.unnamed.iter().enumerate() {
778 let name = format!("_binding_{}", idx);
779 let binding = Ident::new(&name, Span::call_site());
Nika Layzell27726662017-10-24 23:16:35 -0400780
David Tolnay6af48992018-08-01 11:16:28 -0700781 bind_visit_fields.append_all(quote! {
782 ref #binding,
783 });
784 bind_visit_mut_fields.append_all(quote! {
785 ref mut #binding,
786 });
787 bind_fold_fields.append_all(quote! {
788 #binding,
789 });
Nika Layzell27726662017-10-24 23:16:35 -0400790
David Tolnay6af48992018-08-01 11:16:28 -0700791 let borrowed_binding = Borrowed(quote!(#binding));
792 let owned_binding = Owned(quote!(#binding));
Nika Layzell27726662017-10-24 23:16:35 -0400793
David Tolnay6af48992018-08-01 11:16:28 -0700794 visit_fields.append_all(
795 visit(&field.ty, lookup, Visit, &borrowed_binding)
796 .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
797 );
798 visit_mut_fields.append_all(
799 visit(&field.ty, lookup, VisitMut, &borrowed_binding)
800 .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
801 );
802 fold_fields.append_all(
803 visit(&field.ty, lookup, Fold, &owned_binding)
804 .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
805 );
Nika Layzell27726662017-10-24 23:16:35 -0400806
David Tolnay6af48992018-08-01 11:16:28 -0700807 visit_fields.append_all(quote!(;));
808 visit_mut_fields.append_all(quote!(;));
809 fold_fields.append_all(quote!(,));
810 }
Nika Layzell27726662017-10-24 23:16:35 -0400811
David Tolnay6af48992018-08-01 11:16:28 -0700812 visit_variants.append_all(quote! {
813 #ty::#variant_ident(#bind_visit_fields) => {
814 #visit_fields
815 }
816 });
817
818 visit_mut_variants.append_all(quote! {
819 #ty::#variant_ident(#bind_visit_mut_fields) => {
820 #visit_mut_fields
821 }
822 });
823
824 fold_variants.append_all(quote! {
825 #ty::#variant_ident(#bind_fold_fields) => {
826 #ty::#variant_ident(
827 #fold_fields
828 )
829 }
830 });
Nika Layzell27726662017-10-24 23:16:35 -0400831 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500832 Fields::Unit => {
David Tolnay6af48992018-08-01 11:16:28 -0700833 visit_variants.append_all(quote! {
834 #ty::#variant_ident => {}
835 });
836 visit_mut_variants.append_all(quote! {
837 #ty::#variant_ident => {}
838 });
839 fold_variants.append_all(quote! {
840 #ty::#variant_ident => {
841 #ty::#variant_ident
842 }
843 });
Nika Layzell27726662017-10-24 23:16:35 -0400844 }
Nika Layzell27726662017-10-24 23:16:35 -0400845 }
Nika Layzell27726662017-10-24 23:16:35 -0400846 }
David Tolnay6af48992018-08-01 11:16:28 -0700847
848 visit_impl.append_all(quote! {
849 match *_i {
850 #visit_variants
851 }
852 });
853
854 visit_mut_impl.append_all(quote! {
855 match *_i {
856 #visit_mut_variants
857 }
858 });
859
860 fold_impl.append_all(quote! {
861 match _i {
862 #fold_variants
863 }
864 });
Nika Layzell27726662017-10-24 23:16:35 -0400865 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500866 Data::Struct(ref v) => {
David Tolnay6af48992018-08-01 11:16:28 -0700867 let mut fold_fields = TokenStream::new();
868
David Tolnaybc5b2c02018-08-02 00:18:23 -0700869 for (idx, field) in v.fields.iter().enumerate() {
870 let id = match field.ident {
871 Some(ref ident) => Member::Named(ident.clone()),
872 None => Member::Unnamed(Index::from(idx)),
873 };
874 let ref_toks = Owned(quote!(_i.#id));
David Tolnay6af48992018-08-01 11:16:28 -0700875 let visit_field = visit(&field.ty, lookup, Visit, &ref_toks)
876 .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
877 visit_impl.append_all(quote! {
878 #visit_field;
879 });
880 let visit_mut_field = visit(&field.ty, lookup, VisitMut, &ref_toks)
881 .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
882 visit_mut_impl.append_all(quote! {
883 #visit_mut_field;
884 });
David Tolnay83db9272017-12-28 17:02:31 -0500885 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay01ed0ce2018-05-20 20:18:14 -0700886 .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
Nika Layzell27726662017-10-24 23:16:35 -0400887 if let Some(ref name) = field.ident {
David Tolnay6af48992018-08-01 11:16:28 -0700888 fold_fields.append_all(quote! {
889 #name: #fold,
890 });
Nika Layzell27726662017-10-24 23:16:35 -0400891 } else {
David Tolnay6af48992018-08-01 11:16:28 -0700892 fold_fields.append_all(quote! {
893 #fold,
894 });
Nika Layzell27726662017-10-24 23:16:35 -0400895 }
896 }
897
David Tolnaye3d41b72017-12-31 15:24:00 -0500898 match v.fields {
David Tolnay6af48992018-08-01 11:16:28 -0700899 Fields::Named(..) | Fields::Unnamed(..) => fold_impl.append_all(quote! {
900 #ty {
901 #fold_fields
902 }
903 }),
904 Fields::Unit => {
905 if ty == "Ident" {
906 fold_impl.append_all(quote! {
907 let mut _i = _i;
908 let span = _visitor.fold_span(_i.span());
909 _i.set_span(span);
910 });
911 }
912 fold_impl.append_all(quote! {
913 _i
914 });
915 }
Nika Layzell27726662017-10-24 23:16:35 -0400916 };
917 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500918 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400919 }
920
David Tolnay6af48992018-08-01 11:16:28 -0700921 let mut include_fold_impl = true;
David Tolnay360efd22018-01-04 23:35:26 -0800922 if let Data::Struct(ref data) = s.ast.data {
923 if let Fields::Named(ref fields) = data.fields {
David Tolnay01ed0ce2018-05-20 20:18:14 -0700924 if fields
925 .named
926 .iter()
927 .any(|field| field.vis == Visibility::Inherited)
928 {
David Tolnay360efd22018-01-04 23:35:26 -0800929 // Discard the generated impl if there are private fields.
930 // These have to be handwritten.
David Tolnay6af48992018-08-01 11:16:28 -0700931 include_fold_impl = false;
David Tolnay360efd22018-01-04 23:35:26 -0800932 }
933 }
David Tolnayd0adf522017-12-29 01:30:07 -0500934 }
David Tolnay6af48992018-08-01 11:16:28 -0700935
936 state.visit_trait.append_all(quote! {
937 #features
938 fn #visit_fn(&mut self, i: &'ast #ty) {
939 #visit_fn(self, i)
940 }
941 });
942
943 state.visit_impl.append_all(quote! {
944 #features
945 pub fn #visit_fn<'ast, V: Visit<'ast> + ?Sized>(
946 _visitor: &mut V, _i: &'ast #ty
947 ) {
948 #visit_impl
949 }
950 });
951
952 state.visit_mut_trait.append_all(quote! {
953 #features
954 fn #visit_mut_fn(&mut self, i: &mut #ty) {
955 #visit_mut_fn(self, i)
956 }
957 });
958
959 state.visit_mut_impl.append_all(quote! {
960 #features
961 pub fn #visit_mut_fn<V: VisitMut + ?Sized>(
962 _visitor: &mut V, _i: &mut #ty
963 ) {
964 #visit_mut_impl
965 }
966 });
967
968 state.fold_trait.append_all(quote! {
969 #features
970 fn #fold_fn(&mut self, i: #ty) -> #ty {
971 #fold_fn(self, i)
972 }
973 });
974
975 if include_fold_impl {
976 state.fold_impl.append_all(quote! {
977 #features
978 pub fn #fold_fn<V: Fold + ?Sized>(
979 _visitor: &mut V, _i: #ty
980 ) -> #ty {
981 #fold_impl
982 }
983 });
984 }
Nika Layzell27726662017-10-24 23:16:35 -0400985 }
986}
987
David Tolnay6af48992018-08-01 11:16:28 -0700988fn write_file(path: &str, content: TokenStream) {
David Tolnay8c81f622018-07-31 23:34:35 -0700989 let mut file = File::create(path).unwrap();
David Tolnay6af48992018-08-01 11:16:28 -0700990 write!(
991 file,
992 "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
David Tolnay5f794802018-11-24 14:51:21 -0800993 )
994 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -0700995 let mut config = rustfmt::Config::default();
996 config.set().emit_mode(rustfmt::EmitMode::Stdout);
997 config.set().verbose(rustfmt::Verbosity::Quiet);
David Tolnay280202f2018-08-02 00:29:54 -0700998 config.set().format_macro_matchers(true);
David Tolnay53160352019-02-07 23:36:17 +0100999 config.set().normalize_doc_attributes(true);
David Tolnay8c81f622018-07-31 23:34:35 -07001000 let mut session = rustfmt::Session::new(config, Some(&mut file));
David Tolnay6af48992018-08-01 11:16:28 -07001001 session
1002 .format(rustfmt::Input::Text(content.to_string()))
1003 .unwrap();
David Tolnay8c81f622018-07-31 23:34:35 -07001004}
1005
Nika Layzell27726662017-10-24 23:16:35 -04001006fn main() {
1007 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -05001008 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001009
Nika Layzellefb83ba2017-12-19 18:23:55 -05001010 // Load in any terminal types
1011 for &tt in TERMINAL_TYPES {
1012 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -05001013 lookup.insert(
Alex Crichtona74a1c82018-05-16 10:20:44 -07001014 Ident::new(&tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001015 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -05001016 ast: DeriveInput {
Alex Crichtona74a1c82018-05-16 10:20:44 -07001017 ident: Ident::new(tt, Span::call_site()),
David Tolnayd67fb752017-12-27 13:50:29 -05001018 vis: Visibility::Public(VisPublic {
David Tolnay6af48992018-08-01 11:16:28 -07001019 pub_token: <Token![pub]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001020 }),
1021 attrs: vec![],
David Tolnay6af48992018-08-01 11:16:28 -07001022 generics: Generics::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -05001023 data: Data::Struct(DataStruct {
1024 fields: Fields::Unit,
David Tolnay6af48992018-08-01 11:16:28 -07001025 struct_token: <Token![struct]>::default(),
David Tolnayd67fb752017-12-27 13:50:29 -05001026 semi_token: None,
1027 }),
1028 },
hcplaa511792018-05-29 07:13:01 +03001029 features: TokenStream::new(),
David Tolnayd67fb752017-12-27 13:50:29 -05001030 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -05001031 },
David Tolnayd67fb752017-12-27 13:50:29 -05001032 );
Nika Layzellefb83ba2017-12-19 18:23:55 -05001033 }
1034
David Tolnay6af48992018-08-01 11:16:28 -07001035 let mut state = codegen::State::default();
Nika Layzell27726662017-10-24 23:16:35 -04001036 for s in lookup.values() {
1037 codegen::generate(&mut state, &lookup, s);
1038 }
1039
David Tolnay6af48992018-08-01 11:16:28 -07001040 let full_macro = quote! {
1041 #[cfg(feature = "full")]
1042 macro_rules! full {
1043 ($e:expr) => {
1044 $e
1045 };
1046 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001047
David Tolnay6af48992018-08-01 11:16:28 -07001048 #[cfg(all(feature = "derive", not(feature = "full")))]
1049 macro_rules! full {
1050 ($e:expr) => {
1051 unreachable!()
1052 };
1053 }
1054 };
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001055
David Tolnay6af48992018-08-01 11:16:28 -07001056 let skip_macro = quote! {
1057 #[cfg(any(feature = "full", feature = "derive"))]
1058 macro_rules! skip {
1059 ($($tt:tt)*) => {};
1060 }
1061 };
1062
1063 let fold_trait = state.fold_trait;
1064 let fold_impl = state.fold_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001065 write_file(
1066 FOLD_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001067 quote! {
1068 // Unreachable code is generated sometimes without the full feature.
1069 #![allow(unreachable_code)]
Nika Layzell27726662017-10-24 23:16:35 -04001070
David Tolnay6af48992018-08-01 11:16:28 -07001071 use *;
1072 #[cfg(any(feature = "full", feature = "derive"))]
1073 use token::{Brace, Bracket, Paren, Group};
1074 use proc_macro2::Span;
1075 #[cfg(any(feature = "full", feature = "derive"))]
1076 use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -04001077
David Tolnay6af48992018-08-01 11:16:28 -07001078 #full_macro
Nika Layzell27726662017-10-24 23:16:35 -04001079
David Tolnay6af48992018-08-01 11:16:28 -07001080 /// Syntax tree traversal to transform the nodes of an owned syntax tree.
1081 ///
1082 /// See the [module documentation] for details.
1083 ///
1084 /// [module documentation]: index.html
1085 ///
1086 /// *This trait is available if Syn is built with the `"fold"` feature.*
1087 pub trait Fold {
1088 #fold_trait
1089 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001090
David Tolnay6af48992018-08-01 11:16:28 -07001091 #[cfg(any(feature = "full", feature = "derive"))]
1092 macro_rules! fold_span_only {
1093 ($f:ident : $t:ident) => {
1094 pub fn $f<V: Fold + ?Sized>(_visitor: &mut V, mut _i: $t) -> $t {
1095 let span = _visitor.fold_span(_i.span());
1096 _i.set_span(span);
1097 _i
1098 }
1099 }
1100 }
Nika Layzell27726662017-10-24 23:16:35 -04001101
David Tolnay6af48992018-08-01 11:16:28 -07001102 #[cfg(any(feature = "full", feature = "derive"))]
1103 fold_span_only!(fold_lit_byte: LitByte);
1104 #[cfg(any(feature = "full", feature = "derive"))]
1105 fold_span_only!(fold_lit_byte_str: LitByteStr);
1106 #[cfg(any(feature = "full", feature = "derive"))]
1107 fold_span_only!(fold_lit_char: LitChar);
1108 #[cfg(any(feature = "full", feature = "derive"))]
1109 fold_span_only!(fold_lit_float: LitFloat);
1110 #[cfg(any(feature = "full", feature = "derive"))]
1111 fold_span_only!(fold_lit_int: LitInt);
1112 #[cfg(any(feature = "full", feature = "derive"))]
1113 fold_span_only!(fold_lit_str: LitStr);
David Tolnayd0adf522017-12-29 01:30:07 -05001114
David Tolnay6af48992018-08-01 11:16:28 -07001115 #fold_impl
1116 },
David Tolnayae0009e2018-08-01 00:40:00 -07001117 );
Nika Layzell27726662017-10-24 23:16:35 -04001118
David Tolnay6af48992018-08-01 11:16:28 -07001119 let visit_trait = state.visit_trait;
1120 let visit_impl = state.visit_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001121 write_file(
1122 VISIT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001123 quote! {
David Tolnayc1f55792018-11-21 01:39:42 -08001124 #![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
Nika Layzell27726662017-10-24 23:16:35 -04001125
David Tolnay6af48992018-08-01 11:16:28 -07001126 use *;
1127 #[cfg(any(feature = "full", feature = "derive"))]
1128 use punctuated::Punctuated;
1129 use proc_macro2::Span;
1130 #[cfg(any(feature = "full", feature = "derive"))]
1131 use gen::helper::visit::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001132
David Tolnay6af48992018-08-01 11:16:28 -07001133 #full_macro
1134 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001135
David Tolnay6af48992018-08-01 11:16:28 -07001136 /// Syntax tree traversal to walk a shared borrow of a syntax tree.
1137 ///
1138 /// See the [module documentation] for details.
1139 ///
1140 /// [module documentation]: index.html
1141 ///
1142 /// *This trait is available if Syn is built with the `"visit"` feature.*
1143 pub trait Visit<'ast> {
1144 #visit_trait
1145 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001146
David Tolnay6af48992018-08-01 11:16:28 -07001147 #visit_impl
1148 },
David Tolnayae0009e2018-08-01 00:40:00 -07001149 );
Nika Layzell27726662017-10-24 23:16:35 -04001150
David Tolnay6af48992018-08-01 11:16:28 -07001151 let visit_mut_trait = state.visit_mut_trait;
1152 let visit_mut_impl = state.visit_mut_impl;
David Tolnayae0009e2018-08-01 00:40:00 -07001153 write_file(
1154 VISIT_MUT_SRC,
David Tolnay6af48992018-08-01 11:16:28 -07001155 quote! {
David Tolnay6af48992018-08-01 11:16:28 -07001156 use *;
1157 #[cfg(any(feature = "full", feature = "derive"))]
1158 use punctuated::Punctuated;
1159 use proc_macro2::Span;
1160 #[cfg(any(feature = "full", feature = "derive"))]
1161 use gen::helper::visit_mut::*;
David Tolnayf0d63bf2017-12-26 12:29:47 -05001162
David Tolnay6af48992018-08-01 11:16:28 -07001163 #full_macro
1164 #skip_macro
Nika Layzell27726662017-10-24 23:16:35 -04001165
David Tolnay6af48992018-08-01 11:16:28 -07001166 /// Syntax tree traversal to mutate an exclusive borrow of a syntax tree in
1167 /// place.
1168 ///
1169 /// See the [module documentation] for details.
1170 ///
1171 /// [module documentation]: index.html
1172 ///
1173 /// *This trait is available if Syn is built with the `"visit-mut"` feature.*
1174 pub trait VisitMut {
1175 #visit_mut_trait
1176 }
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001177
David Tolnay6af48992018-08-01 11:16:28 -07001178 #visit_mut_impl
1179 },
David Tolnayae0009e2018-08-01 00:40:00 -07001180 );
Nika Layzell27726662017-10-24 23:16:35 -04001181}