1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::spanned::Spanned;
4use syn::{parse_macro_input, FnArg, GenericParam, ItemFn};
5
6#[proc_macro_attribute]
8pub fn warning(_: TokenStream, input: TokenStream) -> TokenStream {
9 let input = parse_macro_input!(input as ItemFn);
10 let fn_name = &input.sig.ident;
11
12 let argument_types = input
13 .sig
14 .inputs
15 .iter()
16 .filter_map(|arg| match arg {
17 FnArg::Receiver(_) => None,
18 FnArg::Typed(arg) => Some(&arg.ty),
19 })
20 .collect::<Vec<_>>();
21 let argument_idents = input
22 .sig
23 .inputs
24 .iter()
25 .enumerate()
26 .filter_map(|(index, arg)| match arg {
27 FnArg::Receiver(_) => None,
28 FnArg::Typed(arg) => Some(syn::Ident::new(&format!("arg{}", index), arg.pat.span())),
29 })
30 .collect::<Vec<_>>();
31
32 let private_mod = format_ident!("__{}", fn_name);
33
34 let vis = &input.vis;
35
36 let (impl_generics, ty_generics, where_clause) = input.sig.generics.split_for_impl();
37 let generics = &input.sig.generics.params;
38 let phantom_data = (!input.sig.generics.params.is_empty()).then(|| {
39 let ty_generics_tuple = input.sig.generics.params.iter().map(|param| match param {
40 GenericParam::Type(ty) => {
41 let ty = &ty.ident;
42 quote!(#ty)
43 }
44 GenericParam::Lifetime(lifetime) => quote!(&#lifetime ()),
45 GenericParam::Const(_) => quote!(()),
46 });
47 quote!(PhantomData(std::marker::PhantomData<(#(#ty_generics_tuple),*)>))
48 });
49
50 let attrs = &input.attrs;
51
52 TokenStream::from(quote! {
54 #[allow(non_camel_case_types)]
55 #(#attrs)*
56 #vis struct #fn_name {}
57
58 mod #private_mod {
59 use super::*;
60
61 pub(crate) enum __Callable<#generics> #where_clause {
62 #[allow(non_camel_case_types)]
63 #fn_name,
64 #phantom_data
65 }
66
67 impl #impl_generics __Callable #ty_generics #where_clause {
68 fn __run_if_enabled(#(#argument_idents: #argument_types),*) {
69 <#fn_name as ::warnings::Warning>::ID.if_enabled(|| {
70 #input
71 #fn_name(#(#argument_idents),*);
72 });
73 }
74 }
75
76 impl #impl_generics std::ops::Deref for __Callable #ty_generics #where_clause {
77 type Target = fn(#(#argument_types),*);
78 fn deref(&self) -> &Self::Target {
79 &(Self::__run_if_enabled as fn(#(#argument_types),*))
80 }
81 }
82 }
83 #vis use #private_mod::__Callable::*;
84
85 impl ::warnings::Warning for #fn_name {
86 const ID: ::warnings::WarningId = ::warnings::WarningId::of::<#fn_name>();
87 }
88 })
89}