delegation_codegen/
lib.rs

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