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}