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}