1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#![recursion_limit = "128"]

extern crate proc_macro;

use quote::quote;

mod codegen;
mod parse;

use syn::visit::Visit;

/// Procedural macro to automatically generate code for the
/// [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern)
///
/// # Example
///
/// ```
/// use visit::visit;
///
/// visit! {
///     #![visitor_trait = "Visitor"]
///
///     struct Bar {
///         a: Child,
///         b: Child,
///     }
///
///     struct Child {}
/// }
///
/// struct MyVisitor;
///
/// impl Visitor for MyVisitor {
///     fn visit_child(&mut self, child: &Child) {
///         // Do something cool
///     }
/// }
///
/// # fn main() {}
/// ```
///
/// Use the `accept` method on the data structure you want to visit:
///
/// ```
/// # use simple::*;
/// #
/// let mut visitor = MyVisitor {};
/// let root = Bar { a: Child {}, b: Child {} };
/// root.accept(&mut visitor);
/// ```
///
/// # Attributes
///
/// ```ignore
/// #![visitor_trait = "Visitor"]
/// ```
///
/// Set the name of the visitor trait that should be generated.
///
/// ```ignore
/// #![visitor_trait_pub = "Visitor"]
/// ```
///
/// Like `#![visitor_trait]`, but the generated trait will be `pub`.
///
/// # Details
///
/// visit automatically generates a visitor trait named by the required `#![visitor_trait]` attribute:
///
/// ```
/// # use simple::{Bar, Child};
/// #
/// trait Visitor {
///     fn visit_bar(&mut self, bar: &Bar) {}
///     fn visit_child(&mut self, child: &Child) {}
///     // ...
/// }
/// ```
///
/// This trait specifies `visit` methods for all supported items (structs and enums) contained inside the `visit!` macro
/// block.
/// It also provides empty default implementations for all methods so you only need to implement the `visit_*` methods
/// that are relevant to your current use case.
///
/// visit also generates an accept visitor trait. It is named `AcceptVisitor` where `Visitor` will be replaced by the
/// name specified using the `#![visitor_trait]` attribute.
///
/// ```
/// # use simple::Visitor;
/// #
/// trait AcceptVisitor {
///     fn accept<V: Visitor>(&self, visitor: &mut V);
/// }
/// ```
///
/// This trait gets automatically implemented for all items contained inside the `visit!` macro block. For example, a
/// trait implementation generated for `Bar` could look like this:
///
/// ```ignore
/// impl AcceptVisitor for Bar {
///     fn accept<V: Visitor>(&self, visitor: &mut V) {
///         self.a.accept(visitor);
///         self.b.accept(visitor);
///         visitor.visit_bar(self);
///     }
/// }
/// ```
///
/// visit also generates some default implementations for common collections and `Option<T>`. Primitive types are
/// ignored (visit generates an empty accept trait implementation for them).
///
#[proc_macro]
pub fn visit(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let mut file: syn::File = syn::parse2(input.into()).unwrap();
    // The only supported inner attribute in the visit macro context is a single `visitor_trait` declaration
    let visitor_trait_info = parse::get_visitor_trait_info(&file);
    let visitor_trait_ident = &visitor_trait_info.ident;
    file.attrs = Vec::new();
    let mut visitor = parse::ASTVisitor::new();
    visitor.visit_file(&file);
    let visitor_trait_gen =
        codegen::generate_visitor_trait(&visitor_trait_info, &visitor.structs, &visitor.enums);
    let accept_trait_ident = codegen::accept_visitor_trait_ident(&visitor_trait_ident);
    let accept_trait_gen =
        codegen::generate_accept_visitor_trait(&visitor_trait_info, &accept_trait_ident);
    let accept_trait_impls =
        codegen::generate_accept_visitor_impls(&visitor_trait_ident, &accept_trait_ident);

    let mut accept_impls = proc_macro2::TokenStream::new();
    for item_struct in visitor.structs {
        let stream = codegen::generate_accept_impl_for_struct(
            &visitor_trait_ident,
            &accept_trait_ident,
            &item_struct,
        );
        accept_impls.extend(stream);
    }
    for item_enum in visitor.enums {
        let stream = codegen::generate_accept_impl_for_enum(
            &visitor_trait_ident,
            &accept_trait_ident,
            &item_enum,
        );
        accept_impls.extend(stream);
    }

    let result = quote! {
        #file
        #visitor_trait_gen
        #accept_trait_gen
        #accept_trait_impls
        #accept_impls
    };
    result.into()
}