with_locals_proc_macros/
mod.rs1#![allow(nonstandard_style)]
2
3extern crate proc_macro;
4
5use ::proc_macro::{
6 TokenStream,
7};
8use ::proc_macro2::{
9 Span,
10 TokenStream as TokenStream2,
11};
12use ::quote::{
13 format_ident,
14 quote,
15 quote_spanned,
16 ToTokens,
17};
18use ::syn::{*,
19 parse::{
20 Parse,
22 Parser,
23 ParseStream,
24 },
25 spanned::Spanned,
27 Result,
28 visit_mut::{self, VisitMut},
29};
30
31use ::core::{
32 mem,
33 ops::Not as _,
34};
35
36use self::{
37 helpers::{Fields as __, FnLike, LifetimeVisitor},
38};
39
40#[macro_use]
41mod helpers;
42
43mod attrs;
44include!("handle_returning_locals.rs");
45mod handle_let_bindings;
46mod wrap_statements_inside_closure_body;
47
48type Str = ::std::borrow::Cow<'static, str>;
49
50use attrs::Attrs;
51
52#[proc_macro_attribute] pub
55fn with (
56 attrs: TokenStream,
57 input: TokenStream,
58) -> TokenStream
59{
60 let ref attrs = parse_macro_input!(attrs as Attrs);
61 #[cfg(feature = "expand-macros")]
62 let mut name = String::new();
63 match parse::<TraitItemMethod>(input.clone()) {
64 | Ok(mut method) => {
65 #[cfg(feature = "expand-macros")] {
66 name = method.sig.ident.to_string();
67 }
68 handle_fn_like(attrs, &mut method, None)
69 .map(|()| method.into_token_stream())
70 },
71 | Err(_) => match parse(input) {
72 | Ok(Item::Impl(item)) => {
73 #[cfg(feature = "expand-macros")] {
74 name = item.self_ty.to_token_stream().to_string();
75 }
76 with_impl(attrs, item)
77 },
78 | Ok(Item::Trait(item)) => {
79 #[cfg(feature = "expand-macros")] {
80 name = item.ident.to_string();
81 }
82 with_trait(attrs, item)
83 },
84 | Ok(Item::Fn(mut fun)) => {
85 #[cfg(feature = "expand-macros")] {
86 name = fun.fields().sig.ident.to_string();
87 }
88 handle_fn_like(attrs, &mut fun, None)
89 .map(|()| fun.into_token_stream())
90 },
91 | _otherwise => Err(Error::new(Span::call_site(), "\
92 `#[with]` can only be applied to \
93 an `fn`, a `trait`, or an `impl`.\
94 ")),
95 },
96 }
97 .map_or_else(
98 |err| err.to_compile_error().into(),
99 |ret| {
100 #[cfg(feature = "expand-macros")] {
101 helpers::pretty_print_tokenstream(
102 &ret,
103 &name,
104 );
105 }
106
107 ret.into()
108 },
109 )
110}
111
112fn handle_fn_like<Fun : FnLike> (
113 attrs: &'_ Attrs,
114 fun: &'_ mut Fun,
115 outer_scope: Option<(&'_ Generics, ::func_wrap::ImplOrTrait<'_>)>
116) -> Result<()>
117{
118 handle_returning_locals(fun, attrs, outer_scope)?;
119 if let Some(block) = fun.fields().block {
120 handle_let_bindings::f(block, attrs)?;
121 }
122 Ok(())
123}
124
125fn with_impl (outer_with_attrs: &'_ Attrs, mut impl_: ItemImpl)
126 -> Result<TokenStream2>
127{
128 let outer_scope = (
129 &impl_.generics,
130 ::func_wrap::ImplOrTrait::ImplMethod {
131 implementor: &impl_.self_ty,
132 trait_name: impl_.trait_.as_ref().map(|(_, it, _)| it)
133 },
134 );
135 impl_.items.iter_mut().try_for_each(|it| match it {
136 | &mut ImplItem::Method(ref mut method) => {
137 let mut attr = None;
138 let mut err = None;
139 method
140 .attrs
141 .retain(|cur_attr| if cur_attr.path.is_ident("with") {
142 let prev = attr.replace(cur_attr.clone());
143 if let Some(prev) = prev {
144 err = Some(Error::new_spanned(prev,
145 "Duplicate `#[with]` attribute",
146 ));
147 }
148 false } else {
150 true
151 })
152 ;
153 if let Some(err) = err { return Err(err); }
154 let storage;
155 let attrs: &Attrs = match attr {
156 Some(attr) => {
157 storage = attr.parse_args::<Attrs>()?;
158 &storage
159 },
160 None => outer_with_attrs,
161 };
162 handle_fn_like(&attrs, method, Some(outer_scope))
163 },
164 | _ => Ok(()),
165 })?;
166 Ok(impl_.into_token_stream())
167}
168
169
170fn with_trait (outer_with_attrs: &'_ Attrs, mut trait_: ItemTrait)
171 -> Result<TokenStream2>
172{
173 let outer_scope = (
174 &trait_.generics,
175 ::func_wrap::ImplOrTrait::DefaultMethod { trait_name: &trait_.ident },
176 );
177 trait_.items.iter_mut().try_for_each(|it| match it {
178 | &mut TraitItem::Method(ref mut method) => {
179 let mut attr = None;
180 let mut err = None;
181 method
182 .attrs
183 .retain(|cur_attr| if cur_attr.path.is_ident("with") {
184 let prev = attr.replace(cur_attr.clone());
185 if let Some(prev) = prev {
186 err = Some(Error::new_spanned(prev,
187 "Duplicate `#[with]` attribute",
188 ));
189 }
190 false } else {
192 true
193 })
194 ;
195 if let Some(err) = err { return Err(err); }
196 let storage;
197 let attrs: &Attrs = match attr {
198 Some(attr) => {
199 storage = attr.parse_args::<Attrs>()?;
200 &storage
201 },
202 None => outer_with_attrs,
203 };
204 handle_fn_like(&attrs, method, Some(outer_scope))
205 },
206 | _ => Ok(()),
207 })?;
208 Ok(trait_.into_token_stream())
209}