1#![forbid(non_ascii_idents)]
3#![deny(
4 future_incompatible,
5 keyword_idents,
6 elided_lifetimes_in_paths,
7 meta_variable_misuse,
8 noop_method_call,
9 pointer_structural_match,
10 unused_lifetimes,
11 unused_qualifications,
12 clippy::wildcard_dependencies,
13 clippy::debug_assert_with_mut_call,
14 clippy::empty_line_after_outer_attr,
15 clippy::panic,
16 clippy::unwrap_used,
17 clippy::redundant_field_names,
18 clippy::rest_pat_in_fully_bound_structs,
19 clippy::unneeded_field_pattern,
20 clippy::useless_let_if_seq
21)]
22#![warn(clippy::pedantic)]
23
24use std::iter;
25
26use proc_macro::{Span, TokenStream};
27
28use proc_macro_error::{abort, proc_macro_error, OptionExt, ResultExt as _};
29use quote::{format_ident, quote};
30
31struct SignatureVisitor;
32
33impl syn::visit_mut::VisitMut for SignatureVisitor {
34 fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) {
35 let replace = match &*i.ident.to_string() {
36 "static" | "fut" => false,
37 "_" => true,
38 _ => {
39 abort!(
40 i,
41 "custom lifetimes in #[name_it] args are not supported yet"
42 );
43 }
44 };
45
46 if replace {
47 i.ident = syn::Ident::new("fut", Span::call_site().into());
48 }
49
50 syn::visit_mut::visit_lifetime_mut(self, i);
51 }
52
53 fn visit_type_reference_mut(&mut self, i: &mut syn::TypeReference) {
54 if i.lifetime.is_none() {
55 i.lifetime = Some(syn::Lifetime::new("'_", Span::call_site().into()));
56 }
57
58 syn::visit_mut::visit_type_reference_mut(self, i);
59 }
60
61 fn visit_type_trait_object_mut(&mut self, i: &mut syn::TypeTraitObject) {
62 let mut found = false;
63 for bound in &i.bounds {
64 if matches!(bound, syn::TypeParamBound::Lifetime(_)) {
65 found = true;
66 break;
67 }
68 }
69
70 if !found {
71 i.bounds
72 .push(syn::TypeParamBound::Lifetime(syn::Lifetime::new(
73 "'_",
74 Span::call_site().into(),
75 )));
76 }
77
78 syn::visit_mut::visit_type_trait_object_mut(self, i);
79 }
80}
81
82fn bump_visibility(vis: syn::Visibility) -> syn::Visibility {
83 let super_ = syn::Ident::new("super", Span::call_site().into());
84 match vis {
85 syn::Visibility::Public(_) | syn::Visibility::Crate(_) => vis,
86 syn::Visibility::Restricted(mut vis) => {
87 let first = vis
88 .path
89 .segments
90 .first_mut()
91 .expect_or_abort("empty path in visibility declration");
92 if first.ident == "self" {
93 first.ident = super_;
94 return syn::Visibility::Restricted(vis);
95 }
96
97 vis.path.segments.insert(
98 0,
99 syn::PathSegment {
100 ident: super_,
101 arguments: syn::PathArguments::None,
102 },
103 );
104 syn::Visibility::Restricted(vis)
105 }
106 syn::Visibility::Inherited => syn::Visibility::Restricted(syn::VisRestricted {
107 pub_token: syn::token::Pub::default(),
108 paren_token: syn::token::Paren::default(),
109 in_token: None,
110 path: Box::new(syn::Path {
111 leading_colon: None,
112 segments: std::iter::once(syn::PathSegment {
113 ident: super_,
114 arguments: syn::PathArguments::None,
115 })
116 .collect(),
117 }),
118 }),
119 }
120}
121
122#[proc_macro_error]
123#[proc_macro_attribute]
124pub fn name_it(attr: TokenStream, func: TokenStream) -> TokenStream {
125 let mut func: syn::ItemFn =
126 syn::parse(func).expect_or_abort("#[name_it] accepts only a function");
127 let type_name: syn::Ident =
128 syn::parse(attr).expect_or_abort("#[name_it] sole argument must be an ident");
129
130 if func.sig.asyncness.is_none() {
131 abort!(func, "#[name_it] only works on async functions");
132 }
133
134 if !func.sig.generics.params.is_empty() {
135 abort!(
136 func.sig.generics,
137 "generics are not supported by #[name_it] yet"
138 );
139 }
140
141 let func_name = func.sig.ident.clone();
142 let mut func_return_type = func.sig.output.clone();
143 if func_return_type == syn::ReturnType::Default {
144 func_return_type = syn::ReturnType::Type(
145 <syn::Token![->]>::default(),
146 Box::new(syn::Type::Tuple(syn::TypeTuple {
147 paren_token: syn::token::Paren::default(),
148 elems: syn::punctuated::Punctuated::new(),
149 })),
150 );
151 }
152
153 let mut wrapped_func = func.clone();
154 wrapped_func.sig.asyncness = None;
155 let fut_lifetime = syn::Lifetime::new("'fut", Span::call_site().into());
156 let mut generics = syn::Generics::default();
157 generics
158 .params
159 .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new(
160 fut_lifetime.clone(),
161 )));
162 wrapped_func.sig.generics = generics;
163 wrapped_func.sig.output = syn::ReturnType::Type(
164 <syn::Token![->]>::default(),
165 Box::new(syn::Type::Path(syn::TypePath {
166 qself: None,
167 path: syn::Path {
168 leading_colon: None,
169 segments: iter::once(syn::PathSegment {
170 ident: type_name.clone(),
171 arguments: syn::PathArguments::AngleBracketed(
172 syn::AngleBracketedGenericArguments {
173 colon2_token: None,
174 lt_token: <syn::Token![<]>::default(),
175 args: [syn::GenericArgument::Lifetime(fut_lifetime)]
176 .into_iter()
177 .collect(),
178 gt_token: <syn::Token![>]>::default(),
179 },
180 ),
181 })
182 .collect(),
183 },
184 })),
185 );
186 let arg_idents = func.sig.inputs.iter().map(|arg| match arg {
187 syn::FnArg::Receiver(_) => {
188 abort!(arg, "methods are not supported by #[name_it]");
189 }
190 syn::FnArg::Typed(pat_type) => match &*pat_type.pat {
191 syn::Pat::Ident(ident)
192 if ident.by_ref.is_none() && ident.subpat.is_none() && ident.attrs.is_empty() =>
193 {
194 if matches!(&*pat_type.ty, syn::Type::ImplTrait(_)) {
195 abort!(pat_type.ty, "generics are not supported by #[name_it] yet");
196 }
197
198 ident.ident.clone()
199 }
200 _ => abort!(
201 arg,
202 "only simple `ident` patterns in function args are supported by #[name_it] for now"
203 ),
204 },
205 });
206 syn::visit_mut::visit_signature_mut(&mut SignatureVisitor, &mut wrapped_func.sig);
207
208 let vis = func.vis.clone();
209 func.vis = bump_visibility(func.vis);
210 let new_vis = func.vis.clone();
211
212 let module_name = format_ident!("_{}_impl", func_name);
213 func.sig.ident = func_name.clone();
214 wrapped_func.block = Box::new(
215 syn::parse(
216 quote! {{
217 let fut = #module_name::#func_name(#(#arg_idents),*);
218 unsafe {
223 let bytes = ::name_it::transmute_generic(fut);
224 ::name_it::Named::new(#module_name::#type_name::new(bytes))
225 }
226 }}
227 .into(),
228 )
229 .expect("failed to parse function block from procmacro, this is a bug"),
230 );
231
232 let underscores = func.sig.inputs.iter().map(|_| syn::TypeInfer {
233 underscore_token: <syn::Token![_]>::default(),
234 });
235
236 quote! {
237 mod #module_name {
238 use super::*;
239
240 #[forbid(elided_lifetimes_in_paths)]
241 #func
242
243 ::name_it::_name_it_inner!(#new_vis type #type_name = #func_name(#(#underscores),*) #func_return_type);
244 }
245
246 #vis type #type_name<'fut> = ::name_it::Named<#module_name::#type_name<'fut>>;
247
248 #[allow(unused_mut)]
249 #wrapped_func
250 }
251 .into()
252}