1#![warn(clippy::large_types_passed_by_value)]
2#![warn(clippy::large_stack_frames)]
3#![warn(clippy::large_futures)]
4#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![cfg_attr(docsrs, allow(unused_attributes))]
7#![doc = include_str!("../README.md")]
8
9#[allow(unused_extern_crates)]
10extern crate proc_macro;
11
12use proc_macro::TokenStream;
13
14#[cfg(feature = "enabled")]
15mod inner {
16 use proc_macro2::TokenStream;
17 use quote::ToTokens;
18 use syn::{
19 GenericParam,
20 ImplItem,
21 Item,
22 ItemFn,
23 ItemImpl,
24 ItemTrait,
25 ReturnType,
26 Signature,
27 TraitItem,
28 Type,
29 TypeParamBound,
30 WherePredicate,
31 };
32
33 fn filter_send_bounds(
34 bounds: impl IntoIterator<Item = TypeParamBound>,
35 ) -> impl IntoIterator<Item = TypeParamBound> {
36 bounds.into_iter().filter(|bound| {
37 if let TypeParamBound::Trait(bound) = bound {
39 !(bound.path.is_ident("Send") || bound.path.is_ident("Sync"))
40 } else {
41 true
42 }
43 })
44 }
45
46 fn rm_from_fn_sig(sig: &mut Signature) {
47 for param in sig.generics.params.iter_mut() {
49 if let GenericParam::Type(ty) = param {
50 ty.bounds = filter_send_bounds(ty.bounds.clone()).into_iter().collect();
51 }
52 }
53 if let Some(ref mut where_clause) = sig.generics.where_clause {
55 for predicate in where_clause.predicates.iter_mut() {
56 if let WherePredicate::Type(predicate) = predicate {
57 predicate.bounds = filter_send_bounds(predicate.bounds.clone()).into_iter().collect();
58 }
59 }
60 }
61 if let ReturnType::Type(_, ref mut ty) = sig.output {
63 if let Type::ImplTrait(ty) = ty.as_mut() {
64 ty.bounds = filter_send_bounds(ty.bounds.clone()).into_iter().collect();
65 }
66 }
67 }
68
69 fn rm_from_item_fn(mut ast: ItemFn) -> TokenStream {
70 rm_from_fn_sig(&mut ast.sig);
71 ast.into_token_stream()
72 }
73
74 fn rm_from_impl(mut ast: ItemImpl) -> TokenStream {
75 for item in ast.items.iter_mut() {
76 if let ImplItem::Fn(item) = item {
77 rm_from_fn_sig(&mut item.sig);
78 }
79 }
80 ast.into_token_stream()
81 }
82
83 fn rm_from_item_trait(mut ast: ItemTrait) -> TokenStream {
84 ast.supertraits = filter_send_bounds(ast.supertraits.clone()).into_iter().collect();
85
86 for item in ast.items.iter_mut() {
87 if let TraitItem::Fn(item) = item {
88 rm_from_fn_sig(&mut item.sig);
89 }
90 }
91
92 ast.into_token_stream()
93 }
94
95 pub(crate) fn rm_send(input: TokenStream) -> TokenStream {
96 match syn::parse2::<Item>(input.clone()) {
97 Ok(Item::Fn(item)) => rm_from_item_fn(item),
98 Ok(Item::Trait(item)) => rm_from_item_trait(item),
99 Ok(Item::Impl(item)) => rm_from_impl(item),
100 _ => input,
101 }
102 }
103}
104
105#[proc_macro_attribute]
197#[cfg(not(feature = "enabled"))]
198pub fn rm_send_if(_: TokenStream, input: TokenStream) -> TokenStream {
199 input
200}
201
202#[cfg(feature = "enabled")]
203fn wrap_cfg_attr(args: TokenStream, modified: TokenStream, input: TokenStream) -> TokenStream {
204 format!("#[cfg({args})]\n{modified}\n#[cfg(not({args}))]\n{input}")
205 .parse()
206 .unwrap()
207}
208
209#[proc_macro_attribute]
301#[cfg(feature = "enabled")]
302pub fn rm_send_if(args: TokenStream, input: TokenStream) -> TokenStream {
303 let modified: TokenStream = inner::rm_send(input.clone().into()).into();
304 wrap_cfg_attr(args, modified, input)
305}
306
307#[cfg(test)]
308#[cfg(feature = "enabled")]
309mod tests {
310 use crate::inner::rm_send;
311 use quote::quote;
312
313 #[test]
314 fn should_remove_basic_send_and_sync() {
315 let input = quote! {
316 pub async fn foo<R, A, B: Baz>(a: B, b: B) -> impl Future<Output = R> + Send
317 where R: Send,
318 A: Foo + Send,
319 B: Bar + Send + Sync
320 {
321 async move { Ok(()) }
322 }
323 };
324 let modified = rm_send(input);
325 assert!(!modified.to_string().contains("Send"));
326 assert!(!modified.to_string().contains("Sync"));
327 }
328
329 #[test]
330 fn should_remove_into_send() {
331 let input = quote! {
332 pub async fn foo<R, A, B>(a: B, b: B) -> impl Future<Output = R> + Send
333 where R: Send,
334 A: Into<Foo> + Send,
335 B: Into<Bar> + Send
336 {
337 async move { Ok(()) }
338 }
339 };
340 let modified = rm_send(input);
341 assert!(!modified.to_string().contains("Send"));
342 }
343
344 #[test]
345 fn should_remove_try_into_send() {
346 let input = quote! {
347 pub async fn foo<R, A, B>(a: B, b: B) -> impl Future<Output = Result<R>> + Send
348 where R: Send,
349 A: TryInto<Foo> + Send,
350 A::Error: Into<Error> + Send,
351 B: TryInto<Bar> + Send,
352 B::Error: Into<Error> + Send
353 {
354 async move { Ok(()) }
355 }
356 };
357 let modified = rm_send(input);
358 assert!(!modified.to_string().contains("Send"));
359 }
360
361 #[test]
362 fn should_remove_fn_args_send() {
363 let input = quote! {
364 pub async fn foo<R, A, B, F>(a: B, b: B) -> impl Future<Output = Result<R>> + Send
365 where R: Send,
366 A: TryInto<Foo> + Send,
367 A::Error: Into<Error> + Send,
368 B: TryInto<Bar> + Send,
369 B::Error: Into<Error> + Send,
370 F: Fn(()) -> Result<(), ()> + Send + 'static
371 {
372 async move { Ok(()) }
373 }
374 };
375 let modified = rm_send(input);
376 assert!(!modified.to_string().contains("Send"));
377 }
378
379 #[test]
380 fn should_remove_trait_impl() {
381 let input = quote! {
382 impl T4 for Foo {
383 fn bar<A, B>(&self, _a: A, _b: B) -> impl Future<Output = ()> + Send
384 where
385 A: T1 + Send,
386 B: T2 + Send + Sync,
387 {
388 async move { () }
389 }
390 }
391 };
392
393 let modified = rm_send(input);
394 assert!(!modified.to_string().contains("Send"));
395 }
396}