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 defaultness,
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(|(bang, path, for_)| quote!(#bang #path #for_));
179 let items = items.into_iter().map(|item| {
180 let func = match item {
181 ImplItem::Fn(f) => f,
182 _ => return item.into_token_stream(),
183 };
184 delegate_fn(func, receiver)
185 });
186
187 quote! {
188 #(#attrs)* #defaultness #unsafety #impl_token #generics #trait_ #self_ty #where_clause {
189 #(#items)*
190 }
191 }
192}
193
194fn delegate_fn(input: ImplItemFn, receiver: &Expr) -> TokenStream {
195 let ImplItemFn {
196 mut attrs,
197 vis,
198 defaultness,
199 sig,
200 block: _,
201 } = input;
202 let mut errors = TokenStream::new();
203 macro_rules! push_error {
204 ($error: expr) => {
205 errors.extend($error.into_compile_error())
206 };
207 ($span: expr, $msg: expr) => {
208 push_error!(syn::Error::new($span, $msg))
209 };
210 }
211 let mut has_inline = false;
213 let mut has_into = false;
214 let mut call_name = None;
215 attrs.retain(|attr| {
216 let path = attr.path();
217 if path.is_ident("inline") {
218 has_inline = true;
219 } else if path.is_ident("into") {
220 match &attr.meta {
221 Meta::List(meta) => {
222 push_error!(meta.delimiter.span().join(), "unexpected argument")
223 }
224 Meta::NameValue(meta) => push_error!(meta.eq_token.span, "unexpected argument"),
225 Meta::Path(_) => {}
226 }
227 if has_into {
228 push_error!(attr.span(), "duplicate #[into] attribute");
229 }
230 has_into = true;
231 return false;
232 } else if path.is_ident("call") {
233 match attr.parse_args::<Ident>() {
234 Ok(ident) => {
235 if call_name.is_some() {
236 push_error!(attr.span(), "duplicate #[call] attribute");
237 }
238 call_name = Some(ident);
239 }
240 Err(e) => push_error!(e),
241 }
242 return false;
243 }
244 true
245 });
246 let inline = if !has_inline {
248 quote!(#[inline(always)])
249 } else {
250 quote!()
251 };
252 let mut inputs = sig.inputs.iter();
253 let self_token = match inputs.next() {
255 Some(FnArg::Receiver(receiver)) => receiver.self_token.to_token_stream(),
256 Some(FnArg::Typed(pat)) => match &*pat.pat {
257 Pat::Ident(ident) if ident.ident == "self" => ident.ident.to_token_stream(),
258 _ => {
259 push_error!(pat.span(), "expected self");
260 TokenStream::new()
261 }
262 },
263 None => {
264 push_error!(sig.paren_token.span.join(), "expected self");
265 TokenStream::new()
266 }
267 };
268 let args = inputs
270 .filter_map(|arg| match arg {
271 FnArg::Typed(pat) => match &*pat.pat {
272 Pat::Ident(ident) => Some(ident.to_token_stream()),
273 _ => {
274 push_error!(pat.pat.span(), "expect an identifier");
275 None
276 }
277 },
278 _ => {
279 push_error!(arg.span(), "unexpected argument");
280 None
281 }
282 })
283 .collect::<Vec<_>>();
284 if !errors.is_empty() {
286 return errors;
287 } else {
288 drop(errors);
290 }
291 let name = call_name.as_ref().unwrap_or(&sig.ident);
293 let receiver = replace_self(receiver.to_token_stream(), &self_token);
296 let body = quote! { #receiver.#name(#(#args),*) };
297 let body = match &sig.output {
298 ReturnType::Default => quote! { #body; },
299 ReturnType::Type(_, ty) if has_into => {
300 quote! { ::std::convert::Into::<#ty>::into(#body) }
301 }
302 _ => body,
303 };
304 quote! {
305 #(#attrs)* #inline #vis #defaultness #sig {
306 #body
307 }
308 }
309}
310
311fn replace_self(expr: TokenStream, self_token: &TokenStream) -> TokenStream {
312 expr.into_iter()
313 .map(|token| match token {
314 TokenTree::Ident(ident) if ident == "self" => self_token.clone(),
315 TokenTree::Group(group) => {
316 let delimiter = group.delimiter();
317 let stream = replace_self(group.stream(), self_token);
318 Group::new(delimiter, stream).into_token_stream()
319 }
320 _ => token.into_token_stream(),
321 })
322 .collect()
323}