Skip to main content

delegation_codegen/
lib.rs

1#![cfg_attr(any(doc, test), doc = include_str!("../README.md"))]
2#![cfg_attr(not(any(doc, test)), doc = env!("CARGO_PKG_NAME"))]
3#![deny(nonstandard_style, rustdoc::all, trivial_casts, trivial_numeric_casts)]
4#![forbid(non_ascii_idents, unsafe_code)]
5#![warn(
6    clippy::absolute_paths,
7    clippy::allow_attributes,
8    clippy::allow_attributes_without_reason,
9    clippy::as_conversions,
10    clippy::as_pointer_underscore,
11    clippy::as_ptr_cast_mut,
12    clippy::assertions_on_result_states,
13    clippy::branches_sharing_code,
14    clippy::cfg_not_test,
15    clippy::clear_with_drain,
16    clippy::clone_on_ref_ptr,
17    clippy::coerce_container_to_any,
18    clippy::collection_is_never_read,
19    clippy::create_dir,
20    clippy::dbg_macro,
21    clippy::debug_assert_with_mut_call,
22    clippy::decimal_literal_representation,
23    clippy::default_union_representation,
24    clippy::derive_partial_eq_without_eq,
25    clippy::doc_include_without_cfg,
26    clippy::doc_paragraphs_missing_punctuation,
27    clippy::empty_drop,
28    clippy::empty_structs_with_brackets,
29    clippy::equatable_if_let,
30    clippy::empty_enum_variants_with_brackets,
31    clippy::exit,
32    clippy::expect_used,
33    clippy::fallible_impl_from,
34    clippy::filetype_is_file,
35    clippy::float_cmp_const,
36    clippy::fn_to_numeric_cast_any,
37    clippy::get_unwrap,
38    clippy::if_then_some_else_none,
39    clippy::imprecise_flops,
40    clippy::infinite_loop,
41    clippy::iter_on_empty_collections,
42    clippy::iter_on_single_items,
43    clippy::iter_over_hash_type,
44    clippy::iter_with_drain,
45    clippy::large_include_file,
46    clippy::large_stack_frames,
47    clippy::let_underscore_untyped,
48    clippy::literal_string_with_formatting_args,
49    clippy::lossy_float_literal,
50    clippy::map_err_ignore,
51    clippy::map_with_unused_argument_over_ranges,
52    clippy::mem_forget,
53    clippy::missing_assert_message,
54    clippy::missing_asserts_for_indexing,
55    clippy::missing_const_for_fn,
56    clippy::missing_docs_in_private_items,
57    clippy::module_name_repetitions,
58    clippy::multiple_inherent_impl,
59    clippy::multiple_unsafe_ops_per_block,
60    clippy::mutex_atomic,
61    clippy::mutex_integer,
62    clippy::needless_collect,
63    clippy::needless_pass_by_ref_mut,
64    clippy::needless_raw_strings,
65    clippy::needless_type_cast,
66    clippy::non_zero_suggestions,
67    clippy::nonstandard_macro_braces,
68    clippy::option_if_let_else,
69    clippy::or_fun_call,
70    clippy::panic_in_result_fn,
71    clippy::partial_pub_fields,
72    clippy::pathbuf_init_then_push,
73    clippy::pedantic,
74    clippy::precedence_bits,
75    clippy::print_stderr,
76    clippy::print_stdout,
77    clippy::pub_without_shorthand,
78    clippy::rc_buffer,
79    clippy::rc_mutex,
80    clippy::read_zero_byte_vec,
81    clippy::redundant_clone,
82    clippy::redundant_test_prefix,
83    clippy::redundant_type_annotations,
84    clippy::renamed_function_params,
85    clippy::ref_patterns,
86    clippy::rest_pat_in_fully_bound_structs,
87    clippy::return_and_then,
88    clippy::same_name_method,
89    clippy::semicolon_inside_block,
90    clippy::set_contains_or_insert,
91    clippy::shadow_unrelated,
92    clippy::significant_drop_in_scrutinee,
93    clippy::significant_drop_tightening,
94    clippy::single_option_map,
95    clippy::str_to_string,
96    clippy::string_add,
97    clippy::string_lit_as_bytes,
98    clippy::string_lit_chars_any,
99    clippy::string_slice,
100    clippy::suboptimal_flops,
101    clippy::suspicious_operation_groupings,
102    clippy::suspicious_xor_used_as_pow,
103    clippy::tests_outside_test_module,
104    clippy::todo,
105    clippy::too_long_first_doc_paragraph,
106    clippy::trailing_empty_array,
107    clippy::transmute_undefined_repr,
108    clippy::trivial_regex,
109    clippy::try_err,
110    clippy::undocumented_unsafe_blocks,
111    clippy::unimplemented,
112    clippy::uninhabited_references,
113    clippy::unnecessary_safety_comment,
114    clippy::unnecessary_safety_doc,
115    clippy::unnecessary_self_imports,
116    clippy::unnecessary_struct_initialization,
117    clippy::unused_peekable,
118    clippy::unused_result_ok,
119    clippy::unused_trait_names,
120    clippy::unwrap_in_result,
121    clippy::unwrap_used,
122    clippy::use_debug,
123    clippy::use_self,
124    clippy::useless_let_if_seq,
125    clippy::verbose_file_reads,
126    clippy::volatile_composites,
127    clippy::while_float,
128    clippy::wildcard_enum_match_arm,
129    ambiguous_negative_literals,
130    closure_returning_async_block,
131    future_incompatible,
132    impl_trait_redundant_captures,
133    let_underscore_drop,
134    macro_use_extern_crate,
135    meta_variable_misuse,
136    missing_copy_implementations,
137    missing_debug_implementations,
138    missing_docs,
139    redundant_lifetimes,
140    rust_2018_idioms,
141    single_use_lifetimes,
142    unit_bindings,
143    unnameable_types,
144    unreachable_pub,
145    unstable_features,
146    unused,
147    variant_size_differences
148)]
149
150mod derive;
151mod impl_for;
152mod impl_trait;
153mod macro_path;
154pub(crate) mod util;
155
156#[cfg(test)]
157#[doc(hidden)]
158mod used_only_in_integrations_tests {
159    use delegation as _;
160    use rustversion as _;
161    use trybuild as _;
162}
163
164use proc_macro2::TokenStream;
165use quote::ToTokens as _;
166use syn::spanned::Spanned as _;
167
168use self::macro_path::MacroPath;
169
170/// Derives trait on a new-type struct or enum, invoking it on its inner type.
171///
172/// # Example
173///
174/// ```rust
175/// # use delegation::delegate;
176/// #
177/// #[delegate(derive(AsString))]
178/// enum Name {
179///     First(FirstName),
180///     Last(LastName),
181/// }
182///
183/// #[delegate(derive(AsString))]
184/// struct FirstName(String);
185///
186/// #[delegate]
187/// struct LastName(String);
188///
189/// #[delegate(for(LastName))]
190/// trait AsString {
191///     fn into_string(self) -> String;
192///     fn as_str(&self) -> &str;
193///     fn as_mut_str(&mut self) -> &mut String;
194/// }
195///
196/// impl AsString for String {
197///     fn into_string(self) -> Self {
198///         self
199///     }
200///     fn as_str(&self) -> &str {
201///         self.as_str()
202///     }
203///     fn as_mut_str(&mut self) -> &mut Self {
204///         self
205///     }
206/// }
207///
208/// let mut name = Name::First(FirstName("John".into()));
209/// assert_eq!(name.as_str(), "John");
210///
211/// name.as_mut_str().push_str("ny");
212/// assert_eq!(name.as_str(), "Johnny");
213/// assert_eq!(name.into_string(), "Johnny");
214/// ```
215///
216/// # Generics
217///
218/// In some cases, a trait or a type requires additional generic parameters to
219/// implement delegation. For this case, macro provides `for<..>` and `where`
220/// syntax for `#[delegate(derive(..))]`/`#[delegate(for(..))]` attribute
221/// arguments. Specified generics will be merged with the existing ones,
222/// provided by the trait/type definition.
223///
224/// ```rust
225/// # use delegation::delegate;
226/// #
227/// #[delegate(for(
228///     for<U> Case2<U>
229///     where
230///         U: Named<N> + 'static,
231/// ))]
232/// trait Named<N> {
233///     fn name(&self) -> N;
234/// }
235///
236/// struct User(String);
237/// impl Named<String> for User {
238///     fn name(&self) -> String {
239///         self.0.clone()
240///     }
241/// }
242///
243/// #[delegate(derive(
244///     for<N> Named<N>
245///     where
246///         U: Named<N> + 'static,
247/// ))]
248/// enum Case1<U> {
249///     User(U),
250/// }
251///
252/// #[delegate]
253/// struct Case2<U>(U);
254///
255/// #[delegate(derive(
256///    Named<String>
257///    where
258///        U: Named<String> + 'static,
259/// ))]
260/// enum Case3<U> {
261///     Case1(Case1<U>),
262///     Case2(Case2<U>),
263/// }
264///
265/// let user1 = Case1::User(User("Alice".to_string()));
266/// assert_eq!(user1.name(), "Alice");
267///
268/// let user2 = Case2(User("Bob".to_string()));
269/// assert_eq!(user2.name(), "Bob");
270///
271/// let user3 = Case3::Case1(Case1::User(User("Charlie".to_string())));
272/// assert_eq!(user3.name(), "Charlie");
273/// ```
274///
275/// # External types
276///
277/// Because the both sides of the delegation should be marked with the
278/// `#[delegate]` attribute, it's impossible to make external type delegatable.
279/// To handle this, the macro provides the `#[delegate(as = my::Def)]`
280/// attribute argument for struct fields and enum variants. It uses the provided
281/// type as known declaration of some external type. Provided type should be
282/// crate-local, and marked with the `#[delegate]` macro, and to provide an
283/// infallible conversion from external type (including reference-to-reference
284/// one).
285///
286/// ```rust
287/// # use delegation::{private::Either, delegate};
288/// #
289/// #[delegate]
290/// trait AsStr {
291///     fn as_str(&self) -> &str;
292/// }
293///
294/// impl AsStr for String {
295///     fn as_str(&self) -> &str {
296///         self
297///     }
298/// }
299///
300/// #[delegate(derive(AsStr))]
301/// enum EitherDef {
302///     Left(String),
303///     Right(String),
304/// }
305///
306/// impl<'a> From<&'a mut Either<String, String>> for &'a mut EitherDef {
307///     fn from(t: &'a mut Either<String, String>) -> Self {
308///         #[expect(unsafe_code, reason = "macro expansion")]
309///         unsafe {
310///             &mut *(t as *mut Either<String, String> as *mut EitherDef)
311///         }
312///     }
313/// }
314///
315/// impl<'a> From<&'a Either<String, String>> for &'a EitherDef {
316///     fn from(t: &'a Either<String, String>) -> Self {
317///         #[expect(unsafe_code, reason = "macro expansion")]
318///         unsafe {
319///             &*(t as *const Either<String, String> as *const EitherDef)
320///         }
321///     }
322/// }
323///
324/// impl From<Either<String, String>> for EitherDef {
325///     fn from(t: Either<String, String>) -> Self {
326///         match t {
327///             Either::Left(t) => EitherDef::Left(t),
328///             Either::Right(t) => EitherDef::Right(t),
329///         }
330///     }
331/// }
332///
333/// #[delegate(derive(AsStr))]
334/// struct EitherString(#[delegate(as = EitherDef)] Either<String, String>);
335///
336/// let left = EitherString(Either::Left("left".to_string()));
337/// let right = EitherString(Either::Right("right".to_string()));
338/// assert_eq!(left.as_str(), "left");
339/// assert_eq!(right.as_str(), "right");
340/// ```
341///
342/// # External traits
343///
344/// Because the both sides of the delegation should be marked with the
345/// `#[delegate]` attribute, it's impossible to make an external trait
346/// delegatable. To handle this, the macro provides the
347/// `#[delegate(as = my::Def)]` attribute argument for traits. It uses the
348/// provided trait as known declaration of some external trait. With this
349/// argument, the macro will generate a wrapper type implementing the external
350/// trait on it, with the name of the expanded "declaration" trait. By using
351/// this wrapper type in `#[delegate(derive(ext::Trait as my::TraitDef))]`
352/// argument, you can delegate external trait to your type.
353///
354/// ```rust
355/// # use delegation::delegate;
356/// #
357/// #[delegate(as = AsRef)]
358/// trait AsRefDef<T: ?Sized> {
359///     fn as_ref(&self) -> &T;
360/// }
361///
362/// #[delegate]
363/// trait AsStr {
364///     fn as_str(&self) -> &str;
365/// }
366///
367/// impl AsStr for String {
368///     fn as_str(&self) -> &str {
369///         self
370///     }
371/// }
372///
373/// #[delegate(as = AsStr)]
374/// trait AsStrDef {
375///     fn as_str(&self) -> &str;
376/// }
377///
378/// #[delegate(derive(
379///     AsRef<str> as AsRefDef,
380///     AsStr as AsStrDef,
381/// ))]
382/// enum Name {
383///     First(String),
384/// }
385///
386/// let name = Name::First("John".to_string());
387/// assert_eq!(name.as_ref(), "John");
388/// assert_eq!(name.as_str(), "John");
389/// ```
390///
391/// # Limitations
392///
393/// - Both struct/enum and trait should be marked with `#[delegate]` macro
394///   attribute.
395/// - Struct or enum variant should contain only single field.
396/// - Trait methods must have an untyped receiver.
397/// - Supertraits or `Self` trait/method bounds except marker traits like
398///   [`Sized`], [`Send`] or [`Sync`] are not supported yet.
399/// - Associated types/constants are not supported yet.
400/// - Lifetimes in methods are limited to be early-bounded in some cases
401///   (see [rust-lang/rust#87803]).
402/// - `Self` type is limited to be used in methods return types.
403///
404/// [rust-lang/rust#87803]: https://github.com/rust-lang/rust/issues/87803
405#[proc_macro_attribute]
406pub fn delegate(
407    attr_args: proc_macro::TokenStream,
408    body: proc_macro::TokenStream,
409) -> proc_macro::TokenStream {
410    expand(attr_args.into(), body.into())
411        .unwrap_or_else(|e| e.to_compile_error())
412        .into()
413}
414
415/// Implements a delegated trait for the provided type.
416///
417/// Actually, this macro is called by `macro_rules!` in the expansion of the
418/// [`delegate`] macro, and only fills an implementation template generated by
419/// it.
420///
421/// [`delegate`]: macro@delegate
422// TODO: Replace this with flat declarative macro, generated by `#[delegate]`,
423//       once `macro_rules!` can handle generics easily.
424#[proc_macro]
425pub fn impl_for(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
426    syn::parse::<impl_for::Definition>(input)
427        .map_or_else(
428            |e| e.to_compile_error(),
429            quote::ToTokens::into_token_stream,
430        )
431        .into()
432}
433
434/// Expands `#[delegate]` macro on the provided `input`.
435fn expand(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
436    let item = syn::parse2::<syn::Item>(input)?;
437    let tokens = match item {
438        syn::Item::Enum(item) => {
439            derive::Definition::parse_enum(item, args)?.into_token_stream()
440        }
441        syn::Item::Struct(item) => {
442            derive::Definition::parse_struct(item, args)?.into_token_stream()
443        }
444        syn::Item::Trait(item) => {
445            impl_trait::Definition::parse(item, args)?.into_token_stream()
446        }
447        syn::Item::Const(_)
448        | syn::Item::ExternCrate(_)
449        | syn::Item::Fn(_)
450        | syn::Item::ForeignMod(_)
451        | syn::Item::Impl(_)
452        | syn::Item::Macro(_)
453        | syn::Item::Mod(_)
454        | syn::Item::Static(_)
455        | syn::Item::TraitAlias(_)
456        | syn::Item::Type(_)
457        | syn::Item::Union(_)
458        | syn::Item::Use(_)
459        | syn::Item::Verbatim(_) => {
460            return Err(syn::Error::new(
461                item.span(),
462                "allowed only on enums, structs and traits",
463            ));
464        }
465        item => {
466            return Err(syn::Error::new(
467                item.span(),
468                format!("unknown `syn::Item`: {item:?}"),
469            ));
470        }
471    };
472
473    Ok(tokens.into_token_stream())
474}