shallowclone_derive/
lib.rs

1mod attributes;
2mod gen_impl;
3mod target_type;
4
5use gen_impl::gen_impl;
6use proc_macro::TokenStream;
7use proc_macro_error::proc_macro_error;
8use quote::quote;
9use syn::parse_macro_input;
10use syn::{DeriveInput, GenericParam};
11use target_type::get_target_type;
12
13#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14enum DeriveType {
15	ShallowClone,
16	MakeOwned,
17}
18
19#[proc_macro_error]
20#[proc_macro_derive(ShallowClone, attributes(shallowclone))]
21pub fn derive_shallowclone(input: TokenStream) -> TokenStream {
22	derive(input, DeriveType::ShallowClone)
23}
24
25#[proc_macro_error]
26#[proc_macro_derive(MakeOwned, attributes(makeowned))]
27pub fn derive_makeowned(input: TokenStream) -> TokenStream {
28	derive(input, DeriveType::MakeOwned)
29}
30
31fn derive(input: TokenStream, derive_type: DeriveType) -> TokenStream {
32	let input = parse_macro_input!(input as DeriveInput);
33
34	let ident = &input.ident;
35
36	let target_type = match derive_type {
37		DeriveType::ShallowClone => get_target_type(&input, derive_type),
38		DeriveType::MakeOwned => get_target_type(&input, derive_type),
39	};
40
41	// i am actually at a loss of words. why do i have to reinvent the wheel every single
42	// time i make a proc macro? why are there no abstractions for common stuff like DERIVING TRAITS
43	// why tf does THIS FUCKING FUNCTION, which I would expect to be made for derive impls,
44	// RETURN THE GENERICS WITH ATTRIBUTES WHICH DONT FUCKING COMPILE. AND WHY ARE THEY
45	// OPAQUE, WHY CANT I ADD EXTRA GENERICS WHICH MY TRAIT MIGHT USE. WHAT THE FUCK IS THIS SHIT
46	let (_, type_generics, where_clause) = input.generics.split_for_impl();
47
48	let mut impl_generics = Vec::new();
49	let mut extra_bounds = Vec::new();
50	for generic in &input.generics.params {
51		let skip = attributes::is_generic_skipped(derive_type, generic);
52
53		match generic {
54			GenericParam::Lifetime(lifetime_param) => {
55				let lifetime = &lifetime_param.lifetime;
56				let bounds = &lifetime_param.bounds;
57
58				impl_generics.push(quote! { #lifetime: #bounds });
59
60				if !skip && derive_type == DeriveType::ShallowClone {
61					extra_bounds.push(quote! { #lifetime: 'shallowclone });
62				}
63			}
64			GenericParam::Type(type_param) => {
65				let name = &type_param.ident;
66				let bounds = &type_param.bounds;
67
68				impl_generics.push(quote! { #name: #bounds });
69
70				if skip {
71					if derive_type == DeriveType::MakeOwned {
72						extra_bounds.push(quote! { #name: 'static });
73					}
74				} else {
75					match derive_type {
76						DeriveType::ShallowClone => {
77							extra_bounds.push(quote! { #name: ShallowClone<'shallowclone> });
78						}
79						DeriveType::MakeOwned => {
80							// the <T as MakeOwned>::Owned must be bound by the same bounds as T
81							// since we are gonna be using it in place of T
82							let orig_bounds = &type_param.bounds;
83
84							extra_bounds.push(quote! { #name: MakeOwned });
85							extra_bounds.push(quote! { <#name as MakeOwned>::Owned: #orig_bounds });
86						}
87					}
88				}
89			}
90			GenericParam::Const(const_param) => {
91				let name = &const_param.ident;
92				let ty = &const_param.ty;
93
94				impl_generics.push(quote! { const #name: #ty });
95			}
96		}
97	}
98
99	if derive_type == DeriveType::MakeOwned {
100		// Since MakeOwned extends Clone, we want to implement it only if Self: Clone
101		// but we cant just write this bound due to whatever reasons when there are lifetimes
102		// because Self in this context comes with the specific lifetimes, and basically
103		// Self<'static> ends up not included in the bound and then it in turn fucks up
104		// the associated type MakeOwned::Owned, which must be Clone, but when we write
105		// Self: Clone, the compiler starts assuming that Self<'static> is not Clone
106		// (as its not included in the bound)
107		// then you would think you could add another bound Self<'static>: Clone, but no,
108		// compiler complains with a bunch of other weird errors.
109		// Long story short this is most likely a bug in the compiler
110
111		// get the generics with all lifetimes changed to 'any
112		let generics = input.generics.params.iter().map(|param| match param {
113			GenericParam::Lifetime(_) => quote! { 'any },
114			GenericParam::Type(t) => {
115				let ident = &t.ident;
116				quote! { #ident }
117			}
118			GenericParam::Const(c) => {
119				let ident = &c.ident;
120				quote! { #ident }
121			}
122		});
123		extra_bounds.push(quote! { for<'any> #ident <#(#generics),*>: Clone });
124	}
125
126	// For the MakeOwned:
127	//   We should also duplicate all bounds in the where clause, replacing T with <T as MakeOwned>::Owned
128	//   but thats quite complicated, so for now we just dont support where clauses
129	//
130	//   Another solution would be to use a #[shallowclone(bound = "")] attribute to specify the bounds
131	//   instead of trying to parse the where clause. hard to tell without any specific cases in mind
132
133	let where_clause = where_clause
134		.map(|c| quote! { #c })
135		.unwrap_or(quote! { where });
136	let where_clause = quote! {
137		#where_clause
138		#(#extra_bounds),*
139	};
140
141	let impl_code = gen_impl(derive_type, &input);
142
143	match derive_type {
144		DeriveType::ShallowClone => quote! {
145			impl<'shallowclone, #(#impl_generics),*> ShallowClone<'shallowclone> for #ident #type_generics
146			#where_clause {
147				type Target = #target_type;
148
149				fn shallow_clone(&'shallowclone self) -> <Self as ShallowClone<'shallowclone>>::Target {
150					#impl_code
151				}
152			}
153		},
154		DeriveType::MakeOwned => quote! {
155			impl<#(#impl_generics),*> MakeOwned for #ident #type_generics
156			#where_clause {
157				type Owned = #target_type;
158
159				fn make_owned(self) -> <Self as MakeOwned>::Owned {
160					#impl_code
161				}
162			}
163		},
164	}
165	.into()
166}