1extern crate proc_macro;
117
118use proc_macro::TokenStream as RawTokenStream;
119use proc_macro2::{Group, Ident, TokenStream, TokenTree};
120use quote::{quote, quote_spanned, ToTokens};
121use syn::spanned::Spanned;
122use syn::{parse_macro_input, Expr, FnArg, ImplItem, ImplItemFn, ItemImpl, Meta, Pat, ReturnType};
123
124#[proc_macro_attribute]
125pub fn delegate(attr: RawTokenStream, item: RawTokenStream) -> RawTokenStream {
126 let receiver = parse_macro_input!(attr as Expr);
127 delegate_input(item.into(), &receiver).into()
128}
129
130fn delegate_input(input: TokenStream, receiver: &Expr) -> TokenStream {
131 if let Ok(input) = syn::parse2::<ItemImpl>(input.clone()) {
132 return delegate_impl_block(input, receiver);
133 }
134 if let Ok(input) = syn::parse2::<ImplItemFn>(input.clone()) {
135 return delegate_fn(input, receiver);
136 }
137 let mut tokens = input.into_iter();
138 let first_non_attr_token = 'outer: loop {
139 match tokens.next() {
140 None => break None,
141 Some(TokenTree::Punct(p)) if p.as_char() == '#' => {}
142 Some(token) => break Some(token),
143 }
144 loop {
145 match tokens.next() {
146 None => break 'outer None,
147 Some(TokenTree::Punct(_)) => {}
148 Some(TokenTree::Group(_)) => continue 'outer,
149 Some(token) => break 'outer Some(token),
150 }
151 }
152 };
153 if let Some(token) = first_non_attr_token {
154 let msg = match &token {
155 TokenTree::Ident(ident) if ident == "impl" => "invalid impl block for #[delegate]",
156 TokenTree::Ident(ident) if ident == "fn" => "invalid method for #[delegate]",
157 _ => "expected an impl block or method inside impl block",
158 };
159 quote_spanned! { token.span() => compile_error!(#msg); }
160 } else {
161 panic!("unexpected eof")
162 }
163}
164
165fn delegate_impl_block(input: ItemImpl, receiver: &Expr) -> TokenStream {
166 let ItemImpl {
167 attrs,
168 modifiers,
169 unsafety,
170 impl_token,
171 mut generics,
172 trait_,
173 self_ty,
174 brace_token: _,
175 items,
176 } = input;
177 let where_clause = generics.where_clause.take();
178 let trait_ = trait_.map(|(path, for_)| quote!(#path #for_));
179 let defaultness = &modifiers.defaultness;
180 let polarity = &modifiers.polarity;
181 let items = items.into_iter().map(|item| {
182 let func = match item {
183 ImplItem::Fn(f) => f,
184 _ => return item.into_token_stream(),
185 };
186 delegate_fn(func, receiver)
187 });
188
189 quote! {
190 #(#attrs)* #defaultness #unsafety #impl_token #generics #polarity #trait_ #self_ty #where_clause {
191 #(#items)*
192 }
193 }
194}
195
196fn delegate_fn(input: ImplItemFn, receiver: &Expr) -> TokenStream {
197 let ImplItemFn {
198 mut attrs,
199 vis,
200 modifiers,
201 sig,
202 block: _,
203 } = input;
204 let mut errors = TokenStream::new();
205 let defaultness = &modifiers.defaultness;
206 macro_rules! push_error {
207 ($error: expr) => {
208 errors.extend($error.into_compile_error())
209 };
210 ($span: expr, $msg: expr) => {
211 push_error!(syn::Error::new($span, $msg))
212 };
213 }
214 let mut has_inline = false;
216 let mut has_into = false;
217 let mut call_name = None;
218 attrs.retain(|attr| {
219 let path = attr.path();
220 if path.is_ident("inline") {
221 has_inline = true;
222 } else if path.is_ident("into") {
223 match &attr.meta {
224 Meta::List(meta) => {
225 push_error!(meta.delimiter.span().join(), "unexpected argument")
226 }
227 Meta::NameValue(meta) => push_error!(meta.eq_token.span, "unexpected argument"),
228 Meta::Path(_) => {}
229 }
230 if has_into {
231 push_error!(attr.span(), "duplicate #[into] attribute");
232 }
233 has_into = true;
234 return false;
235 } else if path.is_ident("call") {
236 match attr.parse_args::<Ident>() {
237 Ok(ident) => {
238 if call_name.is_some() {
239 push_error!(attr.span(), "duplicate #[call] attribute");
240 }
241 call_name = Some(ident);
242 }
243 Err(e) => push_error!(e),
244 }
245 return false;
246 }
247 true
248 });
249 let inline = if !has_inline {
251 quote!(#[inline(always)])
252 } else {
253 quote!()
254 };
255 let mut inputs = sig.inputs.iter();
256 let self_token = match inputs.next() {
258 Some(FnArg::Receiver(receiver)) => receiver.self_token.to_token_stream(),
259 Some(FnArg::Typed(pat)) => match &*pat.pat {
260 Pat::Ident(ident) if ident.ident == "self" => ident.ident.to_token_stream(),
261 _ => {
262 push_error!(pat.span(), "expected self");
263 TokenStream::new()
264 }
265 },
266 None => {
267 push_error!(sig.paren_token.span.join(), "expected self");
268 TokenStream::new()
269 }
270 };
271 let args = inputs
273 .filter_map(|arg| match arg {
274 FnArg::Typed(pat) => match &*pat.pat {
275 Pat::Ident(ident) => Some(ident.to_token_stream()),
276 _ => {
277 push_error!(pat.pat.span(), "expect an identifier");
278 None
279 }
280 },
281 _ => {
282 push_error!(arg.span(), "unexpected argument");
283 None
284 }
285 })
286 .collect::<Vec<_>>();
287 if !errors.is_empty() {
289 return errors;
290 } else {
291 drop(errors);
293 }
294 let name = call_name.as_ref().unwrap_or(&sig.ident);
296 let receiver = replace_self(receiver.to_token_stream(), &self_token);
299 let body = quote! { #receiver.#name(#(#args),*) };
300 let body = match &sig.output {
301 ReturnType::Default => quote! { #body; },
302 ReturnType::Type(_, ty) if has_into => {
303 quote! { ::std::convert::Into::<#ty>::into(#body) }
304 }
305 _ => body,
306 };
307 quote! {
308 #(#attrs)* #inline #vis #defaultness #sig {
309 #body
310 }
311 }
312}
313
314fn replace_self(expr: TokenStream, self_token: &TokenStream) -> TokenStream {
315 expr.into_iter()
316 .map(|token| match token {
317 TokenTree::Ident(ident) if ident == "self" => self_token.clone(),
318 TokenTree::Group(group) => {
319 let delimiter = group.delimiter();
320 let stream = replace_self(group.stream(), self_token);
321 Group::new(delimiter, stream).into_token_stream()
322 }
323 _ => token.into_token_stream(),
324 })
325 .collect()
326}