blob: 5ebe8e580eb8129a2be37ad4b1386d40016409ef [file] [log] [blame]
David Tolnayea9ae892017-12-26 01:44:32 -05001//! This crate automatically generates the definition of the `Visitor`,
2//! `VisitorMut`, and `Folder` 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 Tolnayea9ae892017-12-26 01:44:32 -050013#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]
14
David Tolnayd67fb752017-12-27 13:50:29 -050015#[macro_use]
16extern crate failure;
Nika Layzell27726662017-10-24 23:16:35 -040017extern crate inflections;
David Tolnay2e0dba12017-12-27 01:54:40 -050018extern crate proc_macro2;
David Tolnayd67fb752017-12-27 13:50:29 -050019#[macro_use]
20extern crate quote;
David Tolnay5c4c0b52017-12-28 17:58:54 -050021#[macro_use]
David Tolnayd67fb752017-12-27 13:50:29 -050022extern crate syn;
Nika Layzell27726662017-10-24 23:16:35 -040023
David Tolnayd67fb752017-12-27 13:50:29 -050024use quote::{ToTokens, Tokens};
David Tolnaye3d41b72017-12-31 15:24:00 -050025use syn::{Attribute, Data, DataStruct, DeriveInput, Ident, Item};
David Tolnayd67fb752017-12-27 13:50:29 -050026use failure::{err_msg, Error};
Nika Layzell27726662017-10-24 23:16:35 -040027
David Tolnay8ed806a2017-12-26 01:28:28 -050028use std::io::{Read, Write};
David Tolnayf0d63bf2017-12-26 12:29:47 -050029use std::fmt::{self, Debug};
Nika Layzell27726662017-10-24 23:16:35 -040030use std::fs::File;
31use std::path::Path;
32use std::collections::BTreeMap;
33
34const SYN_CRATE_ROOT: &str = "../src/lib.rs";
35
36const FOLD_SRC: &str = "../src/gen/fold.rs";
37const VISIT_SRC: &str = "../src/gen/visit.rs";
38const VISIT_MUT_SRC: &str = "../src/gen/visit_mut.rs";
39
David Tolnayd67fb752017-12-27 13:50:29 -050040const IGNORED_MODS: &[&str] = &["fold", "visit", "visit_mut"];
Nika Layzell27726662017-10-24 23:16:35 -040041
David Tolnay4ba63a02017-12-28 15:53:05 -050042const EXTRA_TYPES: &[&str] = &["Ident", "Lifetime", "Lit"];
43
44const TERMINAL_TYPES: &[&str] = &["Span"];
Nika Layzellefb83ba2017-12-19 18:23:55 -050045
Nika Layzell27726662017-10-24 23:16:35 -040046fn path_eq(a: &syn::Path, b: &syn::Path) -> bool {
47 if a.global() != b.global() || a.segments.len() != b.segments.len() {
48 return false;
49 }
David Tolnayd67fb752017-12-27 13:50:29 -050050 a.segments
51 .iter()
52 .zip(b.segments.iter())
Nika Layzell27726662017-10-24 23:16:35 -040053 .all(|(a, b)| a.item().ident.as_ref() == b.item().ident.as_ref())
54}
55
56fn get_features(attrs: &[Attribute], mut features: Tokens) -> Tokens {
57 for attr in attrs {
58 if path_eq(&attr.path, &"cfg".into()) {
59 attr.to_tokens(&mut features);
60 }
61 }
62 features
63}
64
65#[derive(Clone)]
66pub struct AstItem {
David Tolnay3d772182017-12-28 17:18:53 -050067 ast: DeriveInput,
Nika Layzell27726662017-10-24 23:16:35 -040068 features: Tokens,
69 // True if this is an ast_enum_of_structs! item with a #full annotation.
70 eos_full: bool,
71}
72
David Tolnayf0d63bf2017-12-26 12:29:47 -050073impl Debug for AstItem {
Nika Layzell27726662017-10-24 23:16:35 -040074 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 f.debug_struct("AstItem")
David Tolnay3d772182017-12-28 17:18:53 -050076 .field("ast", &self.ast)
Nika Layzell27726662017-10-24 23:16:35 -040077 .field("features", &self.features.to_string())
78 .finish()
79 }
80}
81
82// NOTE: BTreeMap is used here instead of HashMap to have deterministic output.
David Tolnayc47f8422017-12-28 17:30:46 -050083type Lookup = BTreeMap<Ident, AstItem>;
Nika Layzell27726662017-10-24 23:16:35 -040084
David Tolnayd67fb752017-12-27 13:50:29 -050085fn load_file<P: AsRef<Path>>(name: P, features: &Tokens, lookup: &mut Lookup) -> Result<(), Error> {
Nika Layzell27726662017-10-24 23:16:35 -040086 let name = name.as_ref();
David Tolnayea9ae892017-12-26 01:44:32 -050087 let parent = name.parent().ok_or_else(|| err_msg("no parent path"))?;
Nika Layzell27726662017-10-24 23:16:35 -040088
89 let mut f = File::open(name)?;
90 let mut src = String::new();
91 f.read_to_string(&mut src)?;
92
93 // Parse the file
David Tolnayd67fb752017-12-27 13:50:29 -050094 let file =
95 syn::parse_file(&src).map_err(|_| format_err!("failed to parse {}", name.display()))?;
Nika Layzell27726662017-10-24 23:16:35 -040096
97 // Collect all of the interesting AstItems declared in this file or submodules.
David Tolnay4ba63a02017-12-28 15:53:05 -050098 'items: for item in file.items {
99 match item {
100 Item::Mod(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400101 // Don't inspect inline modules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800102 if item.content.is_some() {
Nika Layzell27726662017-10-24 23:16:35 -0400103 continue;
104 }
105
106 // We don't want to try to load the generated rust files and
107 // parse them, so we ignore them here.
108 for name in IGNORED_MODS {
David Tolnayc6b55bc2017-11-09 22:48:38 -0800109 if item.ident.as_ref() == *name {
Nika Layzell27726662017-10-24 23:16:35 -0400110 continue 'items;
111 }
112 }
113
114 // Lookup any #[cfg()] attributes on the module and add them to
115 // the feature set.
116 let features = get_features(&item.attrs, features.clone());
117
118 // Look up the submodule file, and recursively parse it.
119 // XXX: Only handles same-directory .rs file submodules.
David Tolnayc6b55bc2017-11-09 22:48:38 -0800120 let path = parent.join(&format!("{}.rs", item.ident.as_ref()));
David Tolnayea9ae892017-12-26 01:44:32 -0500121 load_file(path, &features, lookup)?;
Nika Layzell27726662017-10-24 23:16:35 -0400122 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500123 Item::Macro(item) => {
Nika Layzell27726662017-10-24 23:16:35 -0400124 // Lookip any #[cfg()] attributes directly on the macro
125 // invocation, and add them to the feature set.
126 let features = get_features(&item.attrs, features.clone());
127
128 // Try to parse the AstItem declaration out of the item.
David Tolnay01a77582018-01-01 20:00:51 -0800129 let tts = &item.mac.tts;
David Tolnayc6b55bc2017-11-09 22:48:38 -0800130 let found = if path_eq(&item.mac.path, &"ast_struct".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800131 syn::parse_str::<parsing::AstStruct>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500132 .map_err(|_| err_msg("failed to parse ast_struct"))?
133 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800134 } else if path_eq(&item.mac.path, &"ast_enum".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800135 syn::parse_str::<parsing::AstEnum>(&quote!(#tts).to_string())
David Tolnayd67fb752017-12-27 13:50:29 -0500136 .map_err(|_| err_msg("failed to parse ast_enum"))?
137 .0
David Tolnayc6b55bc2017-11-09 22:48:38 -0800138 } else if path_eq(&item.mac.path, &"ast_enum_of_structs".into()) {
David Tolnay01a77582018-01-01 20:00:51 -0800139 syn::parse_str::<parsing::AstEnumOfStructs>(&quote!(#tts).to_string())
David Tolnay1cf80912017-12-31 18:35:12 -0500140 .map_err(|_| err_msg("failed to parse ast_enum_of_structs"))?
David Tolnayd67fb752017-12-27 13:50:29 -0500141 .0
Nika Layzell27726662017-10-24 23:16:35 -0400142 } else {
David Tolnayd67fb752017-12-27 13:50:29 -0500143 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400144 };
145
146 // Record our features on the parsed AstItems.
147 for mut item in found {
148 features.to_tokens(&mut item.features);
David Tolnay3d772182017-12-28 17:18:53 -0500149 lookup.insert(item.ast.ident, item);
Nika Layzell27726662017-10-24 23:16:35 -0400150 }
151 }
David Tolnay4ba63a02017-12-28 15:53:05 -0500152 Item::Struct(item) => {
153 let ident = item.ident;
154 if EXTRA_TYPES.contains(&ident.as_ref()) {
155 lookup.insert(ident, AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500156 ast: DeriveInput {
David Tolnay4ba63a02017-12-28 15:53:05 -0500157 ident: ident,
158 vis: item.vis,
159 attrs: item.attrs,
160 generics: item.generics,
David Tolnaye3d41b72017-12-31 15:24:00 -0500161 data: Data::Struct(DataStruct {
162 fields: item.fields,
David Tolnay4ba63a02017-12-28 15:53:05 -0500163 struct_token: item.struct_token,
164 semi_token: item.semi_token,
165 }),
166 },
167 features: features.clone(),
168 eos_full: false,
169 });
170 }
171 }
Nika Layzell27726662017-10-24 23:16:35 -0400172 _ => {}
173 }
174 }
175 Ok(())
176}
177
178mod parsing {
179 use super::AstItem;
180
David Tolnay1cf80912017-12-31 18:35:12 -0500181 use syn;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500182 use syn::synom::*;
Nika Layzell27726662017-10-24 23:16:35 -0400183 use syn::*;
Nika Layzell27726662017-10-24 23:16:35 -0400184 use quote::Tokens;
David Tolnay2e0dba12017-12-27 01:54:40 -0500185 use proc_macro2::TokenStream;
Nika Layzell27726662017-10-24 23:16:35 -0400186
187 // Parses #full - returns #[cfg(feature = "full")] if it is present, and
188 // nothing otherwise.
189 named!(full -> (Tokens, bool), map!(option!(do_parse!(
David Tolnayf8db7ba2017-11-11 22:52:16 -0800190 punct!(#) >>
Nika Layzell27726662017-10-24 23:16:35 -0400191 id: syn!(Ident) >>
David Tolnay28c5a462017-12-27 01:59:30 -0500192 cond_reduce!(id == "full", epsilon!()) >>
David Tolnayea9ae892017-12-26 01:44:32 -0500193 ()
Nika Layzell27726662017-10-24 23:16:35 -0400194 )), |s| if s.is_some() {
195 (quote!(#[cfg(feature = "full")]), true)
196 } else {
197 (quote!(), false)
198 }));
199
David Tolnay28c5a462017-12-27 01:59:30 -0500200 named!(manual_extra_traits -> (), do_parse!(
201 punct!(#) >>
202 id: syn!(Ident) >>
203 cond_reduce!(id == "manual_extra_traits", epsilon!()) >>
204 ()
205 ));
206
Nika Layzell27726662017-10-24 23:16:35 -0400207 // Parses a simple AstStruct without the `pub struct` prefix.
208 named!(ast_struct_inner -> AstItem, do_parse!(
209 id: syn!(Ident) >>
210 features: full >>
David Tolnay28c5a462017-12-27 01:59:30 -0500211 option!(manual_extra_traits) >>
David Tolnay2e0dba12017-12-27 01:54:40 -0500212 rest: syn!(TokenStream) >>
Nika Layzell27726662017-10-24 23:16:35 -0400213 (AstItem {
David Tolnay01a77582018-01-01 20:00:51 -0800214 ast: syn::parse_str(&quote! {
David Tolnay2e0dba12017-12-27 01:54:40 -0500215 pub struct #id #rest
David Tolnay01a77582018-01-01 20:00:51 -0800216 }.to_string())?,
Nika Layzell27726662017-10-24 23:16:35 -0400217 features: features.0,
218 eos_full: features.1,
219 })
220 ));
221
222 // ast_struct! parsing
223 pub struct AstStruct(pub Vec<AstItem>);
224 impl Synom for AstStruct {
David Tolnayab919512017-12-30 23:31:51 -0500225 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500226 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800227 keyword!(pub) >>
228 keyword!(struct) >>
Nika Layzell27726662017-10-24 23:16:35 -0400229 res: call!(ast_struct_inner) >>
David Tolnayab919512017-12-30 23:31:51 -0500230 (AstStruct(vec![res]))
231 ));
Nika Layzell27726662017-10-24 23:16:35 -0400232 }
233
234 // ast_enum! parsing
235 pub struct AstEnum(pub Vec<AstItem>);
236 impl Synom for AstEnum {
David Tolnayab919512017-12-30 23:31:51 -0500237 named!(parse -> Self, map!(syn!(DeriveInput), |x| {
Nika Layzell27726662017-10-24 23:16:35 -0400238 AstEnum(vec![AstItem {
David Tolnayab919512017-12-30 23:31:51 -0500239 ast: x,
Nika Layzell27726662017-10-24 23:16:35 -0400240 features: quote!(),
241 eos_full: false,
242 }])
243 }));
244 }
245
246 // A single variant of an ast_enum_of_structs!
247 struct EosVariant {
248 name: Ident,
David Tolnayfcfb9002017-12-28 22:04:29 -0500249 member: Option<Path>,
Nika Layzell27726662017-10-24 23:16:35 -0400250 inner: Option<AstItem>,
251 }
252 named!(eos_variant -> EosVariant, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500253 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800254 keyword!(pub) >>
Nika Layzell27726662017-10-24 23:16:35 -0400255 variant: syn!(Ident) >>
David Tolnayfcfb9002017-12-28 22:04:29 -0500256 member: option!(map!(parens!(alt!(
David Tolnay3d772182017-12-28 17:18:53 -0500257 call!(ast_struct_inner) => { |x: AstItem| (Path::from(x.ast.ident), Some(x)) }
Nika Layzell27726662017-10-24 23:16:35 -0400258 |
259 syn!(Path) => { |x| (x, None) }
David Tolnay8875fca2017-12-31 13:52:37 -0500260 )), |x| x.1)) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800261 punct!(,) >>
Nika Layzell27726662017-10-24 23:16:35 -0400262 (EosVariant {
263 name: variant,
David Tolnayfcfb9002017-12-28 22:04:29 -0500264 member: member.clone().map(|x| x.0),
265 inner: member.map(|x| x.1).unwrap_or_default(),
Nika Layzell27726662017-10-24 23:16:35 -0400266 })
267 ));
268
269 // ast_enum_of_structs! parsing
270 pub struct AstEnumOfStructs(pub Vec<AstItem>);
271 impl Synom for AstEnumOfStructs {
David Tolnayab919512017-12-30 23:31:51 -0500272 named!(parse -> Self, do_parse!(
David Tolnay2c136452017-12-27 14:13:32 -0500273 many0!(Attribute::parse_outer) >>
David Tolnayf8db7ba2017-11-11 22:52:16 -0800274 keyword!(pub) >>
275 keyword!(enum) >>
Nika Layzell27726662017-10-24 23:16:35 -0400276 id: syn!(Ident) >>
David Tolnaye3d41b72017-12-31 15:24:00 -0500277 variants: braces!(many0!(eos_variant)) >>
Nika Layzell27726662017-10-24 23:16:35 -0400278 option!(syn!(Ident)) >> // do_not_generate_to_tokens
279 ({
280 // XXX: This is really gross - we shouldn't have to convert the
281 // tokens to strings to re-parse them.
282 let enum_item = {
David Tolnaye3d41b72017-12-31 15:24:00 -0500283 let variants = variants.1.iter().map(|v| {
Nika Layzell27726662017-10-24 23:16:35 -0400284 let name = v.name;
David Tolnayfcfb9002017-12-28 22:04:29 -0500285 match v.member {
286 Some(ref member) => quote!(#name(#member)),
287 None => quote!(#name),
288 }
Nika Layzell27726662017-10-24 23:16:35 -0400289 });
David Tolnay01a77582018-01-01 20:00:51 -0800290 syn::parse_str(&quote! {
Nika Layzell27726662017-10-24 23:16:35 -0400291 pub enum #id { #(#variants),* }
David Tolnay01a77582018-01-01 20:00:51 -0800292 }.to_string())?
Nika Layzell27726662017-10-24 23:16:35 -0400293 };
294 let mut items = vec![AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500295 ast: enum_item,
Nika Layzell27726662017-10-24 23:16:35 -0400296 features: quote!(),
297 eos_full: false,
298 }];
David Tolnaye3d41b72017-12-31 15:24:00 -0500299 items.extend(variants.1.into_iter().filter_map(|v| v.inner));
Nika Layzell27726662017-10-24 23:16:35 -0400300 AstEnumOfStructs(items)
301 })
David Tolnayab919512017-12-30 23:31:51 -0500302 ));
Nika Layzell27726662017-10-24 23:16:35 -0400303 }
304}
305
306mod codegen {
307 use super::{AstItem, Lookup};
308 use syn::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500309 use syn::punctuated::Punctuated;
David Tolnayd67fb752017-12-27 13:50:29 -0500310 use quote::{ToTokens, Tokens};
David Tolnayf0d63bf2017-12-26 12:29:47 -0500311 use std::fmt::{self, Display};
Nika Layzell27726662017-10-24 23:16:35 -0400312
313 #[derive(Default)]
314 pub struct State {
315 pub visit_trait: String,
316 pub visit_impl: String,
317 pub visit_mut_trait: String,
318 pub visit_mut_impl: String,
319 pub fold_trait: String,
320 pub fold_impl: String,
321 }
322
David Tolnay4a918742017-12-28 16:54:41 -0500323 fn under_name(name: Ident) -> Ident {
Nika Layzell27726662017-10-24 23:16:35 -0400324 use inflections::Inflect;
325 name.as_ref().to_snake_case().into()
326 }
327
David Tolnay3d772182017-12-28 17:18:53 -0500328 enum RelevantType<'a> {
329 Box(&'a Type),
330 Vec(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500331 Punctuated(&'a Type),
David Tolnay3d772182017-12-28 17:18:53 -0500332 Option(&'a Type),
David Tolnayf2cfd722017-12-31 18:02:51 -0500333 Tuple(&'a Punctuated<Type, Token![,]>),
David Tolnay3d772182017-12-28 17:18:53 -0500334 Simple(&'a AstItem),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500335 Token(Tokens),
David Tolnay3d772182017-12-28 17:18:53 -0500336 Pass,
337 }
Nika Layzell27726662017-10-24 23:16:35 -0400338
David Tolnay3d772182017-12-28 17:18:53 -0500339 fn classify<'a>(ty: &'a Type, lookup: &'a Lookup) -> RelevantType<'a> {
340 match *ty {
341 Type::Path(TypePath { qself: None, ref path }) => {
342 let last = path.segments.last().unwrap().into_item();
343 match last.ident.as_ref() {
344 "Box" => RelevantType::Box(first_arg(&last.arguments)),
345 "Vec" => RelevantType::Vec(first_arg(&last.arguments)),
David Tolnayf2cfd722017-12-31 18:02:51 -0500346 "Punctuated" => RelevantType::Punctuated(first_arg(&last.arguments)),
David Tolnay3d772182017-12-28 17:18:53 -0500347 "Option" => RelevantType::Option(first_arg(&last.arguments)),
David Tolnay1e01f9c2017-12-28 20:16:19 -0500348 "Brace" | "Bracket" | "Paren" | "Group" => {
349 RelevantType::Token(last.ident.into_tokens())
350 }
David Tolnay3d772182017-12-28 17:18:53 -0500351 _ => {
352 if let Some(item) = lookup.get(&last.ident) {
353 RelevantType::Simple(item)
354 } else {
355 RelevantType::Pass
356 }
357 }
358 }
Nika Layzell27726662017-10-24 23:16:35 -0400359 }
David Tolnayeadbda32017-12-29 02:33:47 -0500360 Type::Tuple(TypeTuple { ref elems, .. }) => {
361 RelevantType::Tuple(elems)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500362 }
David Tolnay323279a2017-12-29 11:26:32 -0500363 Type::Macro(TypeMacro { ref mac }) if mac.path.segments.last().unwrap().into_item().ident == "Token" => {
David Tolnay1e01f9c2017-12-28 20:16:19 -0500364 RelevantType::Token(mac.into_tokens())
David Tolnaycc0f0372017-12-28 19:11:04 -0500365 }
David Tolnay3d772182017-12-28 17:18:53 -0500366 _ => RelevantType::Pass,
Nika Layzell27726662017-10-24 23:16:35 -0400367 }
368 }
369
370 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
371 enum Kind {
372 Visit,
373 VisitMut,
374 Fold,
375 }
376
David Tolnayf0d63bf2017-12-26 12:29:47 -0500377 enum Operand {
378 Borrowed(Tokens),
379 Owned(Tokens),
380 }
381
David Tolnay83db9272017-12-28 17:02:31 -0500382 use self::Operand::*;
383 use self::Kind::*;
384
David Tolnayf0d63bf2017-12-26 12:29:47 -0500385 impl Operand {
386 fn tokens(&self) -> &Tokens {
387 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500388 Borrowed(ref n) | Owned(ref n) => n,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500389 }
390 }
391
392 fn ref_tokens(&self) -> Tokens {
393 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500394 Borrowed(ref n) => n.clone(),
395 Owned(ref n) => quote!(&#n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500396 }
397 }
398
399 fn ref_mut_tokens(&self) -> Tokens {
400 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500401 Borrowed(ref n) => n.clone(),
402 Owned(ref n) => quote!(&mut #n),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500403 }
404 }
405
406 fn owned_tokens(&self) -> Tokens {
407 match *self {
David Tolnay83db9272017-12-28 17:02:31 -0500408 Borrowed(ref n) => quote!(*#n),
409 Owned(ref n) => n.clone(),
David Tolnayf0d63bf2017-12-26 12:29:47 -0500410 }
411 }
412 }
413
414 impl Display for Operand {
415 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay4e52d8a2017-12-28 15:54:50 -0500416 Display::fmt(self.tokens(), formatter)
David Tolnayf0d63bf2017-12-26 12:29:47 -0500417 }
418 }
419
Nika Layzellc08227a2017-12-04 16:30:17 -0500420 fn first_arg(params: &PathArguments) -> &Type {
Nika Layzell27726662017-10-24 23:16:35 -0400421 let data = match *params {
Nika Layzellc08227a2017-12-04 16:30:17 -0500422 PathArguments::AngleBracketed(ref data) => data,
423 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell27726662017-10-24 23:16:35 -0400424 };
425
David Tolnayd67fb752017-12-27 13:50:29 -0500426 match **data.args
427 .first()
428 .expect("Expected at least 1 type argument here")
429 .item()
430 {
David Tolnayea9ae892017-12-26 01:44:32 -0500431 GenericArgument::Type(ref ty) => ty,
Nika Layzellc08227a2017-12-04 16:30:17 -0500432 _ => panic!("Expected at least 1 type argument here"),
Nika Layzell357885a2017-12-04 15:47:07 -0500433 }
Nika Layzell27726662017-10-24 23:16:35 -0400434 }
435
436 fn simple_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500437 item: &AstItem,
Nika Layzell27726662017-10-24 23:16:35 -0400438 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500439 name: &Operand,
David Tolnay4a918742017-12-28 16:54:41 -0500440 ) -> String {
441 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500442 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500443 "_visitor.visit_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500444 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500445 name = name.ref_tokens(),
446 ),
David Tolnay83db9272017-12-28 17:02:31 -0500447 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500448 "_visitor.visit_{under_name}_mut({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500449 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500450 name = name.ref_mut_tokens(),
451 ),
David Tolnay83db9272017-12-28 17:02:31 -0500452 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500453 "_visitor.fold_{under_name}({name})",
David Tolnay3d772182017-12-28 17:18:53 -0500454 under_name = under_name(item.ast.ident),
David Tolnay4a918742017-12-28 16:54:41 -0500455 name = name.owned_tokens(),
456 ),
Nika Layzell27726662017-10-24 23:16:35 -0400457 }
458 }
459
460 fn box_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500461 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400462 lookup: &Lookup,
463 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500464 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400465 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500466 let name = name.owned_tokens();
David Tolnay39d0a202017-12-28 18:19:00 -0500467 let res = visit(elem, lookup, kind, &Owned(quote!(*#name)))?;
David Tolnay4a918742017-12-28 16:54:41 -0500468 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500469 Fold => format!("Box::new({})", res),
470 Visit | VisitMut => res,
David Tolnay4a918742017-12-28 16:54:41 -0500471 })
Nika Layzell27726662017-10-24 23:16:35 -0400472 }
473
474 fn vec_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500475 elem: &Type,
Nika Layzell27726662017-10-24 23:16:35 -0400476 lookup: &Lookup,
477 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500478 name: &Operand,
Nika Layzell27726662017-10-24 23:16:35 -0400479 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500480 let operand = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500481 Visit | VisitMut => Borrowed(quote!(it)),
482 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500483 };
David Tolnay39d0a202017-12-28 18:19:00 -0500484 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay4a918742017-12-28 16:54:41 -0500485 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500486 Visit => {
David Tolnay3d772182017-12-28 17:18:53 -0500487 format!(
488 "for it in {name} {{ {val} }}",
489 name = name.ref_tokens(),
490 val = val,
491 )
Nika Layzell27726662017-10-24 23:16:35 -0400492 }
David Tolnay83db9272017-12-28 17:02:31 -0500493 VisitMut => {
David Tolnay3d772182017-12-28 17:18:53 -0500494 format!(
495 "for it in {name} {{ {val} }}",
496 name = name.ref_mut_tokens(),
497 val = val,
498 )
499 }
500 Fold => format!(
501 "FoldHelper::lift({name}, |it| {{ {val} }})",
502 name = name.owned_tokens(),
503 val = val,
504 ),
505 })
506 }
507
508 fn delimited_visit(
509 elem: &Type,
510 lookup: &Lookup,
511 kind: Kind,
512 name: &Operand,
513 ) -> Option<String> {
514 let operand = match kind {
515 Visit | VisitMut => Borrowed(quote!(it)),
516 Fold => Owned(quote!(it)),
517 };
David Tolnay39d0a202017-12-28 18:19:00 -0500518 let val = visit(elem, lookup, kind, &operand)?;
David Tolnay3d772182017-12-28 17:18:53 -0500519 Some(match kind {
520 Visit => {
521 format!(
522 "for el in {name} {{ \
523 let it = el.item(); \
524 {val} \
525 }}",
526 name = name.ref_tokens(),
527 val = val,
528 )
529 }
530 VisitMut => {
531 format!(
532 "for mut el in {name} {{ \
533 let it = el.item_mut(); \
534 {val} \
535 }}",
536 name = name.ref_mut_tokens(),
537 val = val,
538 )
David Tolnay4a918742017-12-28 16:54:41 -0500539 }
David Tolnay83db9272017-12-28 17:02:31 -0500540 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500541 "FoldHelper::lift({name}, |it| {{ {val} }})",
542 name = name.owned_tokens(),
543 val = val,
544 ),
545 })
Nika Layzell27726662017-10-24 23:16:35 -0400546 }
547
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400548 fn option_visit(
David Tolnay3d772182017-12-28 17:18:53 -0500549 elem: &Type,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400550 lookup: &Lookup,
551 kind: Kind,
David Tolnayf0d63bf2017-12-26 12:29:47 -0500552 name: &Operand,
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400553 ) -> Option<String> {
David Tolnay4a918742017-12-28 16:54:41 -0500554 let it = match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500555 Visit | VisitMut => Borrowed(quote!(it)),
556 Fold => Owned(quote!(it)),
David Tolnay4a918742017-12-28 16:54:41 -0500557 };
David Tolnay39d0a202017-12-28 18:19:00 -0500558 let val = visit(elem, lookup, kind, &it)?;
David Tolnay4a918742017-12-28 16:54:41 -0500559 Some(match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500560 Visit => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500561 "if let Some(ref it) = {name} {{ {val} }}",
562 name = name.owned_tokens(),
563 val = val,
564 ),
David Tolnay83db9272017-12-28 17:02:31 -0500565 VisitMut => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500566 "if let Some(ref mut it) = {name} {{ {val} }}",
567 name = name.owned_tokens(),
568 val = val,
569 ),
David Tolnay83db9272017-12-28 17:02:31 -0500570 Fold => format!(
David Tolnay4a918742017-12-28 16:54:41 -0500571 "({name}).map(|it| {{ {val} }})",
572 name = name.owned_tokens(),
573 val = val,
574 ),
575 })
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400576 }
577
David Tolnay5c4c0b52017-12-28 17:58:54 -0500578 fn tuple_visit(
David Tolnayf2cfd722017-12-31 18:02:51 -0500579 elems: &Punctuated<Type, Token![,]>,
David Tolnay5c4c0b52017-12-28 17:58:54 -0500580 lookup: &Lookup,
581 kind: Kind,
582 name: &Operand,
583 ) -> Option<String> {
584 let mut code = String::new();
David Tolnayf047c7a2017-12-31 02:29:04 -0500585 for (i, elem) in elems.iter().enumerate() {
David Tolnay5c4c0b52017-12-28 17:58:54 -0500586 let name = name.tokens();
David Tolnay14982012017-12-29 00:49:51 -0500587 let i = Index::from(i);
David Tolnay5c4c0b52017-12-28 17:58:54 -0500588 let it = Owned(quote!((#name).#i));
David Tolnayf047c7a2017-12-31 02:29:04 -0500589 let val = visit(elem.item(), lookup, kind, &it)
David Tolnay5c4c0b52017-12-28 17:58:54 -0500590 .unwrap_or_else(|| noop_visit(kind, &it));
591 code.push_str(&format!(" {}", val));
592 match kind {
593 Fold => code.push(','),
594 Visit | VisitMut => code.push(';'),
595 }
596 code.push('\n');
597 }
598 if code.is_empty() {
599 None
600 } else {
601 Some(match kind {
602 Fold => {
603 format!("(\n{} )", code)
604 }
605 Visit | VisitMut => {
606 format!("\n{} ", code)
607 }
608 })
609 }
610 }
611
David Tolnay1e01f9c2017-12-28 20:16:19 -0500612 fn token_visit(ty: Tokens, kind: Kind, name: &Operand) -> String {
David Tolnaycc0f0372017-12-28 19:11:04 -0500613 match kind {
614 Fold => format!(
David Tolnay1e01f9c2017-12-28 20:16:19 -0500615 "{ty}(tokens_helper(_visitor, &({name}).0))",
616 ty = ty,
David Tolnaycc0f0372017-12-28 19:11:04 -0500617 name = name.owned_tokens(),
618 ),
619 Visit => format!(
620 "tokens_helper(_visitor, &({name}).0)",
621 name = name.ref_tokens(),
622 ),
623 VisitMut => format!(
624 "tokens_helper(_visitor, &mut ({name}).0)",
625 name = name.ref_mut_tokens(),
626 ),
627 }
628 }
629
David Tolnay4a918742017-12-28 16:54:41 -0500630 fn noop_visit(kind: Kind, name: &Operand) -> String {
631 match kind {
David Tolnay83db9272017-12-28 17:02:31 -0500632 Fold => name.owned_tokens().to_string(),
633 Visit | VisitMut => format!("// Skipped field {}", name),
David Tolnay4a918742017-12-28 16:54:41 -0500634 }
635 }
636
637 fn visit(ty: &Type, lookup: &Lookup, kind: Kind, name: &Operand) -> Option<String> {
David Tolnay3d772182017-12-28 17:18:53 -0500638 match classify(ty, lookup) {
639 RelevantType::Box(elem) => {
640 box_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500641 }
David Tolnay3d772182017-12-28 17:18:53 -0500642 RelevantType::Vec(elem) => {
643 vec_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500644 }
David Tolnayf2cfd722017-12-31 18:02:51 -0500645 RelevantType::Punctuated(elem) => {
David Tolnay3d772182017-12-28 17:18:53 -0500646 delimited_visit(elem, lookup, kind, name)
David Tolnay4a918742017-12-28 16:54:41 -0500647 }
David Tolnay3d772182017-12-28 17:18:53 -0500648 RelevantType::Option(elem) => {
649 option_visit(elem, lookup, kind, name)
650 }
David Tolnay5c4c0b52017-12-28 17:58:54 -0500651 RelevantType::Tuple(elems) => {
652 tuple_visit(elems, lookup, kind, name)
653 }
David Tolnay3d772182017-12-28 17:18:53 -0500654 RelevantType::Simple(item) => {
655 let mut res = simple_visit(item, kind, name);
656 Some(if item.eos_full {
657 format!("full!({res})", res = res)
658 } else {
659 res
660 })
661 }
David Tolnay1e01f9c2017-12-28 20:16:19 -0500662 RelevantType::Token(ty) => {
663 Some(token_visit(ty, kind, name))
David Tolnaycc0f0372017-12-28 19:11:04 -0500664 }
David Tolnay3d772182017-12-28 17:18:53 -0500665 RelevantType::Pass => {
666 None
Nika Layzell27726662017-10-24 23:16:35 -0400667 }
668 }
Nika Layzell27726662017-10-24 23:16:35 -0400669 }
670
671 pub fn generate(state: &mut State, lookup: &Lookup, s: &AstItem) {
David Tolnay3d772182017-12-28 17:18:53 -0500672 let under_name = under_name(s.ast.ident);
Nika Layzell27726662017-10-24 23:16:35 -0400673
674 state.visit_trait.push_str(&format!(
675 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500676 fn visit_{under_name}(&mut self, i: &'ast {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500677 visit_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400678 }}\n",
679 features = s.features,
680 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500681 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400682 ));
683 state.visit_mut_trait.push_str(&format!(
684 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400685 fn visit_{under_name}_mut(&mut self, i: &mut {ty}) {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500686 visit_{under_name}_mut(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400687 }}\n",
688 features = s.features,
689 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500690 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400691 ));
692 state.fold_trait.push_str(&format!(
693 "{features}\n\
694 fn fold_{under_name}(&mut self, i: {ty}) -> {ty} {{ \
David Tolnayd67fb752017-12-27 13:50:29 -0500695 fold_{under_name}(self, i) \
Nika Layzell27726662017-10-24 23:16:35 -0400696 }}\n",
697 features = s.features,
698 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500699 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400700 ));
701
702 state.visit_impl.push_str(&format!(
703 "{features}\n\
Nika Layzellc86173a2017-11-18 13:55:22 -0500704 pub fn visit_{under_name}<'ast, V: Visitor<'ast> + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500705 _visitor: &mut V, _i: &'ast {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400706 features = s.features,
707 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500708 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400709 ));
710 state.visit_mut_impl.push_str(&format!(
711 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400712 pub fn visit_{under_name}_mut<V: VisitorMut + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500713 _visitor: &mut V, _i: &mut {ty}) {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400714 features = s.features,
715 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500716 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400717 ));
David Tolnayd0adf522017-12-29 01:30:07 -0500718 let before_fold_impl_len = state.fold_impl.len();
Nika Layzell27726662017-10-24 23:16:35 -0400719 state.fold_impl.push_str(&format!(
720 "{features}\n\
Nika Layzella6f46c42017-10-26 15:26:16 -0400721 pub fn fold_{under_name}<V: Folder + ?Sized>(\
David Tolnayd67fb752017-12-27 13:50:29 -0500722 _visitor: &mut V, _i: {ty}) -> {ty} {{\n",
Nika Layzell27726662017-10-24 23:16:35 -0400723 features = s.features,
724 under_name = under_name,
David Tolnay3d772182017-12-28 17:18:53 -0500725 ty = s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400726 ));
727
728 // XXX: This part is a disaster - I'm not sure how to make it cleaner though :'(
David Tolnaye3d41b72017-12-31 15:24:00 -0500729 match s.ast.data {
730 Data::Enum(ref e) => {
Nika Layzell27726662017-10-24 23:16:35 -0400731 state.visit_impl.push_str(" match *_i {\n");
732 state.visit_mut_impl.push_str(" match *_i {\n");
733 state.fold_impl.push_str(" match _i {\n");
734 for variant in &e.variants {
David Tolnaye3d41b72017-12-31 15:24:00 -0500735 let fields: Vec<(&Field, Tokens)> = match variant.item().fields {
736 Fields::Named(..) => panic!("Doesn't support enum struct variants"),
737 Fields::Unnamed(ref fields) => {
David Tolnay6702ade2017-12-30 23:38:15 -0500738 let binding = format!(" {}::{}(", s.ast.ident, variant.item().ident);
Nika Layzell27726662017-10-24 23:16:35 -0400739 state.visit_impl.push_str(&binding);
740 state.visit_mut_impl.push_str(&binding);
741 state.fold_impl.push_str(&binding);
742
David Tolnaybdafb102018-01-01 19:39:10 -0800743 let res = fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500744 .iter()
745 .enumerate()
746 .map(|(idx, el)| {
747 let name = format!("_binding_{}", idx);
Nika Layzell27726662017-10-24 23:16:35 -0400748
David Tolnayd67fb752017-12-27 13:50:29 -0500749 state.visit_impl.push_str("ref ");
750 state.visit_mut_impl.push_str("ref mut ");
Nika Layzell27726662017-10-24 23:16:35 -0400751
David Tolnayd67fb752017-12-27 13:50:29 -0500752 state.visit_impl.push_str(&name);
753 state.visit_mut_impl.push_str(&name);
754 state.fold_impl.push_str(&name);
755 state.visit_impl.push_str(", ");
756 state.visit_mut_impl.push_str(", ");
757 state.fold_impl.push_str(", ");
Nika Layzell27726662017-10-24 23:16:35 -0400758
David Tolnayd67fb752017-12-27 13:50:29 -0500759 let mut tokens = quote!();
760 Ident::from(name).to_tokens(&mut tokens);
Nika Layzell27726662017-10-24 23:16:35 -0400761
David Tolnayd67fb752017-12-27 13:50:29 -0500762 (*el.item(), tokens)
763 })
764 .collect();
Nika Layzell27726662017-10-24 23:16:35 -0400765
766 state.visit_impl.push_str(") => {\n");
767 state.visit_mut_impl.push_str(") => {\n");
768 state.fold_impl.push_str(") => {\n");
769
770 res
771 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500772 Fields::Unit => {
David Tolnayd67fb752017-12-27 13:50:29 -0500773 state
774 .visit_impl
David Tolnay6702ade2017-12-30 23:38:15 -0500775 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.item().ident));
David Tolnayd67fb752017-12-27 13:50:29 -0500776 state
777 .visit_mut_impl
David Tolnay6702ade2017-12-30 23:38:15 -0500778 .push_str(&format!(" {0}::{1} => {{ }}\n", s.ast.ident, variant.item().ident));
Nika Layzell27726662017-10-24 23:16:35 -0400779 state.fold_impl.push_str(&format!(
David Tolnay6702ade2017-12-30 23:38:15 -0500780 " {0}::{1} => {{ {0}::{1} }}\n",
781 s.ast.ident,
Nika Layzell27726662017-10-24 23:16:35 -0400782 variant.item().ident
783 ));
David Tolnayd67fb752017-12-27 13:50:29 -0500784 continue;
Nika Layzell27726662017-10-24 23:16:35 -0400785 }
786 };
787
788 if fields.is_empty() {
789 state.visit_impl.push_str(" {}");
790 state.visit_mut_impl.push_str(") => {\n");
791 state.fold_impl.push_str(") => {\n");
792 }
David Tolnayd67fb752017-12-27 13:50:29 -0500793 state
794 .fold_impl
David Tolnay6702ade2017-12-30 23:38:15 -0500795 .push_str(&format!(" {}::{} (\n", s.ast.ident, variant.item().ident,));
Nika Layzell27726662017-10-24 23:16:35 -0400796 for (field, binding) in fields {
797 state.visit_impl.push_str(&format!(
798 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500799 visit(
800 &field.ty,
801 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500802 Visit,
803 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500804 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500805 Visit,
806 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500807 )),
Nika Layzell27726662017-10-24 23:16:35 -0400808 ));
809 state.visit_mut_impl.push_str(&format!(
810 " {};\n",
David Tolnayd67fb752017-12-27 13:50:29 -0500811 visit(
812 &field.ty,
813 lookup,
David Tolnay83db9272017-12-28 17:02:31 -0500814 VisitMut,
815 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500816 ).unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500817 VisitMut,
818 &Borrowed(binding.clone())
David Tolnay4a918742017-12-28 16:54:41 -0500819 )),
Nika Layzell27726662017-10-24 23:16:35 -0400820 ));
821 state.fold_impl.push_str(&format!(
822 " {},\n",
David Tolnay83db9272017-12-28 17:02:31 -0500823 visit(&field.ty, lookup, Fold, &Owned(binding.clone()))
David Tolnay4a918742017-12-28 16:54:41 -0500824 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500825 Fold,
826 &Owned(binding)
David Tolnay4a918742017-12-28 16:54:41 -0500827 )),
Nika Layzell27726662017-10-24 23:16:35 -0400828 ));
829 }
830 state.fold_impl.push_str(" )\n");
831
832 state.visit_impl.push_str(" }\n");
833 state.visit_mut_impl.push_str(" }\n");
834 state.fold_impl.push_str(" }\n");
835 }
836 state.visit_impl.push_str(" }\n");
837 state.visit_mut_impl.push_str(" }\n");
838 state.fold_impl.push_str(" }\n");
839 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500840 Data::Struct(ref v) => {
841 let fields: Vec<(&Field, Tokens)> = match v.fields {
842 Fields::Named(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500843 state
844 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500845 .push_str(&format!(" {} {{\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800846 fields.named
David Tolnayd67fb752017-12-27 13:50:29 -0500847 .iter()
848 .map(|el| {
849 let id = el.item().ident;
850 (*el.item(), quote!(_i.#id))
851 })
852 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400853 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500854 Fields::Unnamed(ref fields) => {
David Tolnayd67fb752017-12-27 13:50:29 -0500855 state
856 .fold_impl
David Tolnay3d772182017-12-28 17:18:53 -0500857 .push_str(&format!(" {} (\n", s.ast.ident));
David Tolnaybdafb102018-01-01 19:39:10 -0800858 fields.unnamed
David Tolnayd67fb752017-12-27 13:50:29 -0500859 .iter()
860 .enumerate()
861 .map(|(idx, el)| {
David Tolnay14982012017-12-29 00:49:51 -0500862 let id = Index::from(idx);
David Tolnayd67fb752017-12-27 13:50:29 -0500863 (*el.item(), quote!(_i.#id))
864 })
865 .collect()
Nika Layzell27726662017-10-24 23:16:35 -0400866 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500867 Fields::Unit => {
Nika Layzellefb83ba2017-12-19 18:23:55 -0500868 state.fold_impl.push_str(" _i\n");
869 vec![]
870 }
Nika Layzell27726662017-10-24 23:16:35 -0400871 };
872
873 for (field, ref_toks) in fields {
David Tolnay83db9272017-12-28 17:02:31 -0500874 let ref_toks = Owned(ref_toks);
Nika Layzell27726662017-10-24 23:16:35 -0400875 state.visit_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500876 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500877 visit(&field.ty, lookup, Visit, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500878 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500879 Visit,
David Tolnay4a918742017-12-28 16:54:41 -0500880 &ref_toks,
881 ))
Nika Layzell27726662017-10-24 23:16:35 -0400882 ));
883 state.visit_mut_impl.push_str(&format!(
David Tolnayd67fb752017-12-27 13:50:29 -0500884 " {};\n",
David Tolnay83db9272017-12-28 17:02:31 -0500885 visit(&field.ty, lookup, VisitMut, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500886 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500887 VisitMut,
David Tolnay4a918742017-12-28 16:54:41 -0500888 &ref_toks,
889 ))
Nika Layzell27726662017-10-24 23:16:35 -0400890 ));
David Tolnay83db9272017-12-28 17:02:31 -0500891 let fold = visit(&field.ty, lookup, Fold, &ref_toks)
David Tolnay4a918742017-12-28 16:54:41 -0500892 .unwrap_or_else(|| noop_visit(
David Tolnay83db9272017-12-28 17:02:31 -0500893 Fold,
David Tolnay4a918742017-12-28 16:54:41 -0500894 &ref_toks,
895 ));
Nika Layzell27726662017-10-24 23:16:35 -0400896 if let Some(ref name) = field.ident {
David Tolnayd67fb752017-12-27 13:50:29 -0500897 state
898 .fold_impl
899 .push_str(&format!(" {}: {},\n", name, fold));
Nika Layzell27726662017-10-24 23:16:35 -0400900 } else {
901 state.fold_impl.push_str(&format!(" {},\n", fold));
902 }
903 }
904
David Tolnaye3d41b72017-12-31 15:24:00 -0500905 match v.fields {
906 Fields::Named(..) => state.fold_impl.push_str(" }\n"),
907 Fields::Unnamed(..) => state.fold_impl.push_str(" )\n"),
908 Fields::Unit => {}
Nika Layzell27726662017-10-24 23:16:35 -0400909 };
910 }
David Tolnaye3d41b72017-12-31 15:24:00 -0500911 Data::Union(..) => panic!("Union not supported"),
Nika Layzell27726662017-10-24 23:16:35 -0400912 }
913
914 // Close the impl body
915 state.visit_impl.push_str("}\n");
916 state.visit_mut_impl.push_str("}\n");
917 state.fold_impl.push_str("}\n");
David Tolnayd0adf522017-12-29 01:30:07 -0500918
919 if s.ast.ident == "Ident" || s.ast.ident == "Lifetime" {
920 // Discard the generated impl. These have private fields and are
921 // handwritten.
922 state.fold_impl.truncate(before_fold_impl_len);
923 }
Nika Layzell27726662017-10-24 23:16:35 -0400924 }
925}
926
927fn main() {
928 let mut lookup = BTreeMap::new();
David Tolnayea9ae892017-12-26 01:44:32 -0500929 load_file(SYN_CRATE_ROOT, &quote!(), &mut lookup).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -0400930
Nika Layzellefb83ba2017-12-19 18:23:55 -0500931 // Load in any terminal types
932 for &tt in TERMINAL_TYPES {
933 use syn::*;
David Tolnayd67fb752017-12-27 13:50:29 -0500934 lookup.insert(
935 Ident::from(tt),
936 AstItem {
David Tolnay3d772182017-12-28 17:18:53 -0500937 ast: DeriveInput {
David Tolnayd67fb752017-12-27 13:50:29 -0500938 ident: Ident::from(tt),
939 vis: Visibility::Public(VisPublic {
940 pub_token: Default::default(),
941 }),
942 attrs: vec![],
943 generics: Default::default(),
David Tolnaye3d41b72017-12-31 15:24:00 -0500944 data: Data::Struct(DataStruct {
945 fields: Fields::Unit,
David Tolnayd67fb752017-12-27 13:50:29 -0500946 struct_token: Default::default(),
947 semi_token: None,
948 }),
949 },
950 features: Default::default(),
951 eos_full: false,
Nika Layzellefb83ba2017-12-19 18:23:55 -0500952 },
David Tolnayd67fb752017-12-27 13:50:29 -0500953 );
Nika Layzellefb83ba2017-12-19 18:23:55 -0500954 }
955
Nika Layzell27726662017-10-24 23:16:35 -0400956 let mut state = Default::default();
957 for s in lookup.values() {
958 codegen::generate(&mut state, &lookup, s);
959 }
960
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400961 let full_macro = "
962#[cfg(feature = \"full\")]
963macro_rules! full {
964 ($e:expr) => { $e }
965}
966
967#[cfg(not(feature = \"full\"))]
968macro_rules! full {
969 ($e:expr) => { unreachable!() }
970}
971";
972
Nika Layzell27726662017-10-24 23:16:35 -0400973 let mut fold_file = File::create(FOLD_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -0500974 write!(
975 fold_file,
976 "\
Nika Layzell27726662017-10-24 23:16:35 -0400977// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
978
979//! A Folder represents an AST->AST fold; it accepts an AST piece,
980//! and returns a piece of the same type.
981
David Tolnay0afc9b32017-12-27 13:38:24 -0500982#![cfg_attr(rustfmt, rustfmt_skip)]
983
Nika Layzell27726662017-10-24 23:16:35 -0400984// Unreachable code is generated sometimes without the full feature.
985#![allow(unreachable_code)]
David Tolnayf0d63bf2017-12-26 12:29:47 -0500986#![cfg_attr(feature = \"cargo-clippy\", allow(needless_pass_by_value))]
Nika Layzell27726662017-10-24 23:16:35 -0400987
Nika Layzella6f46c42017-10-26 15:26:16 -0400988use *;
David Tolnay1e01f9c2017-12-28 20:16:19 -0500989use token::{{Brace, Bracket, Paren, Group}};
David Tolnay98942562017-12-26 21:24:35 -0500990use proc_macro2::Span;
David Tolnayf60f4262017-12-28 19:17:58 -0500991use gen::helper::fold::*;
Nika Layzell27726662017-10-24 23:16:35 -0400992
Nika Layzell4ab8d6e2017-10-26 09:45:49 -0400993{full_macro}
994
Nika Layzell27726662017-10-24 23:16:35 -0400995/// AST->AST fold.
996///
997/// Each method of the Folder trait is a hook to be potentially overridden. Each
998/// method's default implementation recursively visits the substructure of the
999/// input via the `walk` functions, which perform an \"identity fold\", that
1000/// is, they return the same structure that they are given (for example the
1001/// `fold_file` method by default calls `fold::walk_file`).
1002///
1003/// If you want to ensure that your code handles every variant
1004/// explicitly, you need to override each method. (And you also need
1005/// to monitor future changes to `Folder` in case a new method with a
1006/// new default implementation gets introduced.)
1007pub trait Folder {{
1008{fold_trait}
1009}}
1010
David Tolnayd0adf522017-12-29 01:30:07 -05001011pub fn fold_ident<V: Folder + ?Sized>(_visitor: &mut V, mut _i: Ident) -> Ident {{
1012 _i.span = _visitor.fold_span(_i.span);
1013 _i
1014}}
1015
1016pub fn fold_lifetime<V: Folder + ?Sized>(_visitor: &mut V, mut _i: Lifetime) -> Lifetime {{
1017 _i.span = _visitor.fold_span(_i.span);
1018 _i
1019}}
1020
Nika Layzell27726662017-10-24 23:16:35 -04001021{fold_impl}
1022",
David Tolnayd67fb752017-12-27 13:50:29 -05001023 full_macro = full_macro,
1024 fold_trait = state.fold_trait,
1025 fold_impl = state.fold_impl
1026 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001027
1028 let mut visit_file = File::create(VISIT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001029 write!(
1030 visit_file,
1031 "\
Nika Layzell27726662017-10-24 23:16:35 -04001032// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1033
1034//! AST walker. Each overridden visit method has full control over what
1035//! happens with its node, it can do its own traversal of the node's children,
1036//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1037//! deeper traversal by doing nothing.
1038
David Tolnay0afc9b32017-12-27 13:38:24 -05001039#![cfg_attr(rustfmt, rustfmt_skip)]
1040
David Tolnayf0d63bf2017-12-26 12:29:47 -05001041#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1042
Nika Layzella6f46c42017-10-26 15:26:16 -04001043use *;
David Tolnay98942562017-12-26 21:24:35 -05001044use proc_macro2::Span;
David Tolnaycc0f0372017-12-28 19:11:04 -05001045use gen::helper::visit::*;
Nika Layzell27726662017-10-24 23:16:35 -04001046
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001047{full_macro}
1048
Nika Layzell27726662017-10-24 23:16:35 -04001049/// Each method of the Visitor trait is a hook to be potentially
1050/// overridden. Each method's default implementation recursively visits
1051/// the substructure of the input via the corresponding `walk` method;
1052/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1053///
1054/// If you want to ensure that your code handles every variant
1055/// explicitly, you need to override each method. (And you also need
1056/// to monitor future changes to `Visitor` in case a new method with a
1057/// new default implementation gets introduced.)
Nika Layzellc86173a2017-11-18 13:55:22 -05001058pub trait Visitor<'ast> {{
Nika Layzell27726662017-10-24 23:16:35 -04001059{visit_trait}
1060}}
1061
1062{visit_impl}
1063",
David Tolnayd67fb752017-12-27 13:50:29 -05001064 full_macro = full_macro,
1065 visit_trait = state.visit_trait,
1066 visit_impl = state.visit_impl
1067 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001068
1069 let mut visit_mut_file = File::create(VISIT_MUT_SRC).unwrap();
David Tolnayd67fb752017-12-27 13:50:29 -05001070 write!(
1071 visit_mut_file,
1072 "\
Nika Layzell27726662017-10-24 23:16:35 -04001073// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT
1074
1075//! AST walker. Each overridden visit method has full control over what
1076//! happens with its node, it can do its own traversal of the node's children,
1077//! call `visit::walk_*` to apply the default traversal algorithm, or prevent
1078//! deeper traversal by doing nothing.
1079
David Tolnay0afc9b32017-12-27 13:38:24 -05001080#![cfg_attr(rustfmt, rustfmt_skip)]
1081
David Tolnayf0d63bf2017-12-26 12:29:47 -05001082#![cfg_attr(feature = \"cargo-clippy\", allow(match_same_arms))]
1083
Nika Layzella6f46c42017-10-26 15:26:16 -04001084use *;
David Tolnay98942562017-12-26 21:24:35 -05001085use proc_macro2::Span;
David Tolnaycc0f0372017-12-28 19:11:04 -05001086use gen::helper::visit_mut::*;
Nika Layzell27726662017-10-24 23:16:35 -04001087
Nika Layzell4ab8d6e2017-10-26 09:45:49 -04001088{full_macro}
1089
Nika Layzell27726662017-10-24 23:16:35 -04001090/// Each method of the VisitorMut trait is a hook to be potentially
1091/// overridden. Each method's default implementation recursively visits
1092/// the substructure of the input via the corresponding `walk` method;
1093/// e.g. the `visit_mod` method by default calls `visit::walk_mod`.
1094///
1095/// If you want to ensure that your code handles every variant
1096/// explicitly, you need to override each method. (And you also need
1097/// to monitor future changes to `VisitorMut` in case a new method with a
1098/// new default implementation gets introduced.)
1099pub trait VisitorMut {{
1100{visit_mut_trait}
1101}}
1102
1103{visit_mut_impl}
1104",
David Tolnayd67fb752017-12-27 13:50:29 -05001105 full_macro = full_macro,
1106 visit_mut_trait = state.visit_mut_trait,
1107 visit_mut_impl = state.visit_mut_impl
1108 ).unwrap();
Nika Layzell27726662017-10-24 23:16:35 -04001109}