1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use proc_macro2::{Ident, Span, TokenStream}; |
4 | use quote::{quote, ToTokens}; |
5 | use syn::{ |
6 | parse::{Parse, ParseStream}, |
7 | spanned::Spanned, |
8 | Attribute, ExprClosure, Token, |
9 | }; |
10 | |
11 | use crate::{ |
12 | clone::{Capture, CaptureKind, UpgradeBehaviour}, |
13 | utils::crate_ident_new, |
14 | }; |
15 | |
16 | struct Closure { |
17 | captures: Vec<Capture>, |
18 | args: Vec<Ident>, |
19 | upgrade_behaviour: UpgradeBehaviour, |
20 | closure: ExprClosure, |
21 | constructor: &'static str, |
22 | } |
23 | |
24 | impl Parse for Closure { |
25 | fn parse(input: ParseStream) -> syn::Result<Self> { |
26 | let mut captures: Vec<Capture> = vec![]; |
27 | let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None; |
28 | |
29 | loop { |
30 | // There must either be one or no attributes here. Multiple attributes are not |
31 | // supported. |
32 | // |
33 | // If this is a capture attribute, it must be followed by an identifier. |
34 | // If this is an upgrade failure attribute, it might be followed by a closure. After the |
35 | // upgrade failure attribute there must not be any further attributes. |
36 | // |
37 | // If this is not an attribute then it is a closure, async closure or async block which |
38 | // is handled outside the loop |
39 | let attrs = input.call(Attribute::parse_outer)?; |
40 | if attrs.is_empty() { |
41 | break; |
42 | }; |
43 | |
44 | if let Some(capture) = Capture::maybe_parse(&attrs, input)? { |
45 | if capture.kind == CaptureKind::Watch |
46 | && captures.iter().any(|c| c.kind == CaptureKind::Watch) |
47 | { |
48 | return Err(syn::Error::new_spanned( |
49 | &attrs[0], |
50 | "only one `watch` capture is allowed per closure" , |
51 | )); |
52 | } |
53 | |
54 | captures.push(capture); |
55 | } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? { |
56 | if upgrade_behaviour.is_some() { |
57 | return Err(syn::Error::new_spanned( |
58 | &attrs[0], |
59 | "multiple upgrade failure attributes are not supported" , |
60 | )); |
61 | } |
62 | |
63 | upgrade_behaviour = Some((behaviour, attrs[0].span())); |
64 | break; |
65 | } else if let Some(ident) = attrs[0].path().get_ident() { |
66 | return Err(syn::Error::new_spanned( |
67 | &attrs[0], |
68 | format!( |
69 | "unsupported attribute ` {ident}`: only `watch`, `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported" , |
70 | ), |
71 | )); |
72 | } else { |
73 | return Err(syn::Error::new_spanned( |
74 | &attrs[0], |
75 | "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported" , |
76 | )); |
77 | } |
78 | } |
79 | |
80 | if let Some((_, ref span)) = upgrade_behaviour { |
81 | if captures.iter().all(|c| c.kind != CaptureKind::Weak) { |
82 | return Err(syn::Error::new( |
83 | *span, |
84 | "upgrade failure attribute can only be used together with weak variable captures" , |
85 | )); |
86 | } |
87 | } |
88 | |
89 | let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default(); |
90 | |
91 | let mut closure = input.parse::<ExprClosure>()?; |
92 | if closure.asyncness.is_some() { |
93 | return Err(syn::Error::new_spanned( |
94 | closure, |
95 | "async closures not supported" , |
96 | )); |
97 | } |
98 | if !captures.is_empty() && closure.capture.is_none() { |
99 | return Err(syn::Error::new_spanned( |
100 | closure, |
101 | "closures need to capture variables by move. Please add the `move` keyword" , |
102 | )); |
103 | } |
104 | closure.capture = None; |
105 | |
106 | let args = closure |
107 | .inputs |
108 | .iter() |
109 | .enumerate() |
110 | .map(|(i, _)| Ident::new(&format!("____value {i}" ), Span::call_site())) |
111 | .collect(); |
112 | |
113 | // Trailing comma, if any |
114 | if input.peek(Token![,]) { |
115 | input.parse::<Token![,]>()?; |
116 | } |
117 | |
118 | Ok(Closure { |
119 | captures, |
120 | args, |
121 | upgrade_behaviour, |
122 | closure, |
123 | constructor: "new" , |
124 | }) |
125 | } |
126 | } |
127 | |
128 | impl ToTokens for Closure { |
129 | fn to_tokens(&self, tokens: &mut TokenStream) { |
130 | let crate_ident = crate_ident_new(); |
131 | |
132 | let closure_ident = Ident::new("____closure" , Span::call_site()); |
133 | let values_ident = Ident::new("____values" , Span::call_site()); |
134 | let upgrade_failure_closure_ident = |
135 | Ident::new("____upgrade_failure_closure" , Span::call_site()); |
136 | let upgrade_failure_closure_wrapped_ident = |
137 | Ident::new("____upgrade_failure_closure_wrapped" , Span::call_site()); |
138 | |
139 | let outer_before = self |
140 | .captures |
141 | .iter() |
142 | .map(|c| c.outer_before_tokens(&crate_ident)); |
143 | let inner_before = self.captures.iter().map(|c| { |
144 | c.inner_before_tokens( |
145 | &crate_ident, |
146 | &self.upgrade_behaviour, |
147 | &upgrade_failure_closure_wrapped_ident, |
148 | Some(quote! { |
149 | return #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(()); |
150 | }), |
151 | ) |
152 | }); |
153 | let outer_after = self |
154 | .captures |
155 | .iter() |
156 | .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident)); |
157 | |
158 | let arg_values = self.args.iter().enumerate().map(|(index, arg)| { |
159 | let err_msg = format!("Wrong type for argument {index}: {{:? }}" ); |
160 | quote! { |
161 | let #arg = ::core::result::Result::unwrap_or_else( |
162 | #crate_ident::Value::get(&#values_ident[#index]), |
163 | |e| panic!(#err_msg, e), |
164 | ); |
165 | } |
166 | }); |
167 | let arg_names = &self.args; |
168 | let args_len = self.args.len(); |
169 | let closure = &self.closure; |
170 | let constructor = Ident::new(self.constructor, Span::call_site()); |
171 | |
172 | let upgrade_failure_closure = match self.upgrade_behaviour { |
173 | UpgradeBehaviour::Default => Some(quote! { |
174 | let #upgrade_failure_closure_ident = ::std::default::Default::default; |
175 | let #upgrade_failure_closure_wrapped_ident = || |
176 | #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( |
177 | (#upgrade_failure_closure_ident)() |
178 | ); |
179 | }), |
180 | UpgradeBehaviour::Expression(ref expr) => Some(quote! { |
181 | let #upgrade_failure_closure_ident = move || { |
182 | #expr |
183 | }; |
184 | let #upgrade_failure_closure_wrapped_ident = || |
185 | #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( |
186 | (#upgrade_failure_closure_ident)() |
187 | ); |
188 | }), |
189 | UpgradeBehaviour::Closure(ref closure_2) => Some(quote! { |
190 | let #upgrade_failure_closure_ident = #closure_2; |
191 | let #upgrade_failure_closure_wrapped_ident = || |
192 | #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( |
193 | (#upgrade_failure_closure_ident)() |
194 | ); |
195 | }), |
196 | _ => None, |
197 | }; |
198 | |
199 | let assert_return_type = upgrade_failure_closure.is_some().then(|| { |
200 | quote! { |
201 | fn ____same<T>(_a: &T, _b: impl Fn() -> T) {} |
202 | ____same(&____res, #upgrade_failure_closure_ident); |
203 | } |
204 | }); |
205 | |
206 | tokens.extend(quote! { |
207 | { |
208 | let #closure_ident = { |
209 | #(#outer_before)* |
210 | #crate_ident::closure::RustClosure::#constructor(move |#values_ident| { |
211 | assert_eq!( |
212 | #values_ident.len(), |
213 | #args_len, |
214 | "Expected {} arguments but got {}" , |
215 | #args_len, |
216 | #values_ident.len(), |
217 | ); |
218 | #upgrade_failure_closure |
219 | #(#inner_before)* |
220 | #(#arg_values)* |
221 | #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value({ |
222 | let ____res = (#closure)(#(#arg_names),*); |
223 | #assert_return_type |
224 | ____res |
225 | }) |
226 | } |
227 | ) |
228 | }; |
229 | #(#outer_after)* |
230 | #closure_ident |
231 | } |
232 | }); |
233 | } |
234 | } |
235 | |
236 | pub(crate) fn closure_inner( |
237 | input: proc_macro::TokenStream, |
238 | constructor: &'static str, |
239 | ) -> proc_macro::TokenStream { |
240 | let mut closure: Closure = syn::parse_macro_input!(input as Closure); |
241 | closure.constructor = constructor; |
242 | closure.into_token_stream().into() |
243 | } |
244 | |