c2rust_macros/
lib.rs

1#![recursion_limit = "128"]
2extern crate proc_macro;
3extern crate proc_macro2;
4extern crate quote;
5extern crate syn;
6
7use proc_macro2::{Span, TokenStream};
8use quote::quote;
9use syn::parse_macro_input;
10use syn::visit::Visit;
11use syn::{Block, FnArg, Ident, Pat, TraitItemMethod, Type, TypeReference};
12
13#[derive(Default)]
14struct VisitorImpls {
15    tokens: TokenStream,
16    count: usize,
17}
18
19impl VisitorImpls {
20    fn generate_visit(
21        &mut self,
22        method_name: &Ident,
23        arg_pat: &Pat,
24        ty: &Type,
25        walk: &Block,
26    ) {
27        self.tokens.extend(quote! {
28            impl MutVisit for #ty {
29                fn visit<F: MutVisitor>(&mut self, f: &mut F) {
30                    f.#method_name(self)
31                }
32            }
33        });
34
35        let folder_name = format!("Folder{}", self.count);
36        let folder_ident = Ident::new(&folder_name, Span::call_site());
37
38        if !walk.stmts.is_empty() {
39            let noop_fn_name = format!("noop_{}", method_name);
40            let noop_fn = Ident::new(&noop_fn_name, Span::call_site());
41            self.tokens.extend(quote! {
42                impl WalkAst for #ty {
43                    fn walk<T: MutVisitor>(&mut self, v: &mut T) {
44                        syntax::mut_visit::#noop_fn(self, v);
45                    }
46                }
47            });
48        }
49        self.tokens.extend(quote! {
50            struct #folder_ident<F>
51                where F: FnMut(&mut #ty)
52            {
53                callback: F,
54            }
55
56            impl<F> MutVisitor for #folder_ident<F>
57                where F: FnMut(&mut #ty)
58            {
59                fn #method_name(&mut self, #arg_pat: &mut #ty) {
60                    #walk
61                    (self.callback)(#arg_pat)
62                }
63            }
64
65            impl MutVisitNodes for #ty {
66                fn visit<T, F>(target: &mut T, callback: F)
67                    where T: MutVisit,
68                          F: FnMut(&mut Self)
69                {
70                    let mut f = #folder_ident { callback };
71                    target.visit(&mut f)
72                }
73            }
74        });
75
76        self.count += 1;
77    }
78
79    fn generate_flat_map(
80        &mut self,
81        method_name: &Ident,
82        arg_pat: &Pat,
83        ty: &Type,
84        walk: &Block,
85    ) {
86        self.tokens.extend(quote! {
87            impl MutVisit for #ty {
88                fn visit<F: MutVisitor>(&mut self, f: &mut F) {
89                    let new = f.#method_name(self.clone());
90                    *self = new.lone();
91                }
92
93                fn flat_map<F: MutVisitor>(self, f: &mut F) -> SmallVec<[#ty; 1]> {
94                    f.#method_name(self)
95                }
96            }
97        });
98
99        let folder_name = format!("Folder{}", self.count);
100        let folder_ident = Ident::new(&folder_name, Span::call_site());
101
102        if !walk.stmts.is_empty() {
103            let noop_fn_name = format!("noop_{}", method_name);
104            let noop_fn = Ident::new(&noop_fn_name, Span::call_site());
105            self.tokens.extend(quote! {
106                impl WalkAst for #ty {
107                    fn walk<T: MutVisitor>(&mut self, v: &mut T) {
108                        *self = syntax::mut_visit::#noop_fn(self.clone(), v).lone();
109                    }
110                }
111            })
112        }
113        self.tokens.extend(quote! {
114            struct #folder_ident<F>
115                where F: FnMut(#ty) -> SmallVec<[#ty; 1]>
116            {
117                callback: F,
118            }
119
120            impl<F> MutVisitor for #folder_ident<F>
121                where F: FnMut(#ty) -> SmallVec<[#ty; 1]>
122            {
123                fn #method_name(&mut self, #arg_pat: #ty) -> SmallVec<[#ty; 1]> {
124                    let mut v = #walk;
125                    v.flat_map_in_place(|x| (self.callback)(x));
126                    v
127                }
128            }
129
130            impl FlatMapNodes for #ty {
131                fn visit<T, F>(target: &mut T, callback: F)
132                    where T: MutVisit,
133                          F: FnMut(#ty) -> SmallVec<[#ty; 1]>
134                {
135                    let mut f = #folder_ident { callback };
136                    target.visit(&mut f)
137                }
138
139                fn flat_map<T, F>(target: T, callback: F) -> SmallVec<[T; 1]>
140                    where T: MutVisit,
141                          F: FnMut(#ty) -> SmallVec<[#ty; 1]>
142                {
143                    let mut f = #folder_ident { callback };
144                    target.flat_map(&mut f)
145                }
146            }
147        });
148
149        self.count += 1;
150    }
151}
152
153impl<'ast> Visit<'ast> for VisitorImpls {
154    fn visit_trait_item_method(&mut self, m: &TraitItemMethod) {
155        let method_name = &m.sig.ident;
156        let method_noop = m.default.as_ref().unwrap();
157        match &m.sig.inputs[1] {
158            FnArg::Typed(pat_ty) => match &*pat_ty.ty {
159                Type::Reference(TypeReference {
160                    mutability: Some(_),
161                    elem,
162                    ..
163                }) => self.generate_visit(method_name, &pat_ty.pat, &elem, method_noop),
164
165                ty => self.generate_flat_map(method_name, &pat_ty.pat, &ty, method_noop),
166            },
167
168            _ => {}
169        }
170    }
171}
172
173#[proc_macro]
174pub fn gen_visitor_impls(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
175    let visitor_trait: syn::ItemTrait = parse_macro_input!(tokens);
176    let mut visitor = VisitorImpls::default();
177    visitor.visit_item_trait(&visitor_trait);
178
179    visitor.tokens.into()
180}