trait_set/lib.rs
1//! This crate provides support for [trait aliases][alias]: a feature
2//! that is already supported by the Rust compiler, but is [not stable][tracking_issue]
3//! yet.
4//!
5//! The idea is simple: combine a group of traits under a single name. The simplest
6//! example is:
7//!
8//! ```rust
9//! use trait_set::trait_set;
10//!
11//! trait_set! {
12//! pub trait ThreadSafe = Send + Sync;
13//! }
14//! ```
15//!
16//! The [`trait_set`] macro displayed here is the main entity of the crate:
17//! it allows declaring multiple trait aliases, each of which is represented
18//! as:
19//!
20//! ```text
21//! [visibility] trait [AliasName][<generics>] = [Element1] + [Element2] + ... + [ElementN];
22//! ```
23//!
24//! For more details, see the [`trait_set`] macro documentation.
25//!
26//! [alias]: https://doc.rust-lang.org/unstable-book/language-features/trait-alias.html
27//! [tracking_issue]: https://github.com/rust-lang/rust/issues/41517
28//! [`trait_set`]: macro.trait_set.html
29
30use std::iter::FromIterator;
31
32use proc_macro::TokenStream;
33use proc_macro2::TokenStream as TokenStream2;
34use quote::quote;
35use syn::{
36 parse::{Error, Parse, ParseStream},
37 parse_macro_input,
38 punctuated::Punctuated,
39 spanned::Spanned,
40 Attribute, Generics, Ident, Meta, Result, Token, TypeTraitObject, Visibility,
41};
42
43/// Represents one trait alias.
44struct TraitSet {
45 attributes: Vec<Attribute>,
46 implementation_attributes: Vec<Attribute>,
47 visibility: Visibility,
48 _trait_token: Token![trait],
49 alias_name: Ident,
50 generics: Generics,
51 _eq_token: Token![=],
52 traits: TypeTraitObject,
53}
54
55impl TraitSet {
56 /// Selects configuration attributes that must also guard the generated
57 /// implementation. Other attributes describe the user-facing trait and
58 /// may not be valid on a trait implementation.
59 fn implementation_attributes(attributes: &[Attribute]) -> Result<Vec<Attribute>> {
60 attributes
61 .iter()
62 .map(|attribute| Self::configuration_meta(&attribute.meta))
63 .filter_map(Result::transpose)
64 .map(|meta| meta.map(|meta| syn::parse_quote!(#[#meta])))
65 .collect()
66 }
67
68 /// Retains only the item-existence part of an attribute. In particular,
69 /// `cfg_attr` can contain attributes such as `deprecated` that belong on
70 /// the trait but would be rejected on its blanket implementation.
71 fn configuration_meta(meta: &Meta) -> Result<Option<Meta>> {
72 if meta.path().is_ident("cfg") {
73 return Ok(Some(meta.clone()));
74 }
75 if !meta.path().is_ident("cfg_attr") {
76 return Ok(None);
77 }
78
79 let Meta::List(list) = meta else {
80 return Err(Error::new(meta.span(), "expected `cfg_attr(...)`"));
81 };
82 let arguments = list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
83 let mut arguments = arguments.iter();
84 let Some(predicate) = arguments.next() else {
85 return Err(Error::new(meta.span(), "missing `cfg_attr` predicate"));
86 };
87 let configuration_attributes = arguments
88 .map(Self::configuration_meta)
89 .collect::<Result<Vec<_>>>()?
90 .into_iter()
91 .flatten()
92 .collect::<Vec<_>>();
93
94 Ok(if configuration_attributes.is_empty() {
95 None
96 } else {
97 Some(syn::parse_quote! {
98 cfg_attr(#predicate, #(#configuration_attributes),*)
99 })
100 })
101 }
102
103 /// Renders a trait alias into a new trait with bounds set.
104 fn render(self) -> TokenStream2 {
105 // Generic and non-generic implementations have slightly different
106 // syntax, so it's simpler to process them individually rather than
107 // try to generalize the implementation.
108 if self.generics.params.is_empty() {
109 self.render_non_generic()
110 } else {
111 self.render_generic()
112 }
113 }
114
115 /// Renders the trait alias without generic parameters.
116 fn render_non_generic(self) -> TokenStream2 {
117 let attributes = self.attributes;
118 let implementation_attributes = self.implementation_attributes;
119 let visibility = self.visibility;
120 let alias_name = self.alias_name;
121 let bounds = self.traits.bounds;
122 quote! {
123 #(#attributes)*
124 #visibility trait #alias_name: #bounds {}
125
126 #(#implementation_attributes)*
127 impl<_INNER> #alias_name for _INNER where _INNER: #bounds {}
128 }
129 }
130
131 /// Renders the trait alias with generic parameters.
132 fn render_generic(self) -> TokenStream2 {
133 let attributes = self.attributes;
134 let implementation_attributes = self.implementation_attributes;
135 let visibility = self.visibility;
136 let alias_name = self.alias_name;
137 let bounds = self.traits.bounds;
138 let generics = self.generics;
139
140 // Syn owns the distinction between parameter declarations (`T: Send`,
141 // `const N: usize`) and their use as arguments (`T`, `N`). Besides
142 // preserving every supported generic form, `split_for_impl` removes
143 // defaults where Rust forbids them in an impl declaration.
144 let mut implementation_generics = generics.clone();
145 implementation_generics
146 .params
147 .push(syn::parse_quote!(__TRAIT_SET_INNER));
148 let (implementation_generics, _, _) = implementation_generics.split_for_impl();
149 let (_, alias_generics, _) = generics.split_for_impl();
150
151 quote! {
152 #(#attributes)*
153 #visibility trait #alias_name #generics: #bounds {}
154
155 #(#implementation_attributes)*
156 impl #implementation_generics #alias_name #alias_generics for __TRAIT_SET_INNER
157 where
158 __TRAIT_SET_INNER: #bounds
159 {}
160 }
161 }
162}
163
164impl Parse for TraitSet {
165 fn parse(input: ParseStream) -> Result<Self> {
166 let attributes = input.call(Attribute::parse_outer)?;
167 let implementation_attributes = Self::implementation_attributes(&attributes)?;
168 let result = TraitSet {
169 attributes,
170 implementation_attributes,
171 visibility: input.parse()?,
172 _trait_token: input.parse()?,
173 alias_name: input.parse()?,
174 generics: input.parse()?,
175 _eq_token: input.parse()?,
176 traits: input.parse()?,
177 };
178
179 if let Some(where_clause) = result.generics.where_clause {
180 return Err(Error::new(
181 where_clause.span(),
182 "Where clause is not allowed for trait alias",
183 ));
184 }
185 Ok(result)
186 }
187}
188
189/// Represents a sequence of trait aliases delimited by semicolons.
190struct ManyTraitSet {
191 entries: Punctuated<TraitSet, Token![;]>,
192}
193
194impl Parse for ManyTraitSet {
195 fn parse(input: ParseStream) -> Result<Self> {
196 Ok(ManyTraitSet {
197 entries: input.parse_terminated(TraitSet::parse, Token![;])?,
198 })
199 }
200}
201
202impl ManyTraitSet {
203 fn render(self) -> TokenStream2 {
204 TokenStream2::from_iter(self.entries.into_iter().map(|entry| entry.render()))
205 }
206}
207
208/// Creates an alias for a set of traits.
209///
210/// To demonstrate the idea, see the examples:
211///
212/// ```rust
213/// use trait_set::trait_set;
214///
215/// trait_set! {
216/// /// Doc comments are also supported, btw.
217/// pub trait ThreadSafe = Send + Sync;
218/// pub trait ThreadSafeIterator<T> = ThreadSafe + Iterator<Item = T>;
219/// pub trait ThreadSafeBytesIterator = ThreadSafeIterator<u8>;
220/// pub trait StaticDebug = 'static + std::fmt::Debug;
221/// }
222/// ```
223///
224/// This macro also supports [higher-rank trait bounds][hrtb]:
225///
226/// ```rust
227/// # pub trait Serializer {
228/// # type Ok;
229/// # type Error;
230/// #
231/// # fn ok_value() -> Self::Ok;
232/// # }
233/// # pub trait Deserializer<'de> {
234/// # type Error;
235/// # }
236/// #
237/// # pub trait Serialize {
238/// # fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
239/// # where
240/// # S: Serializer;
241/// # }
242/// #
243/// # pub trait Deserialize<'de>: Sized {
244/// # fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245/// # where
246/// # D: Deserializer<'de>;
247/// # }
248/// #
249/// # impl Serializer for u8 {
250/// # type Ok = ();
251/// # type Error = ();
252/// #
253/// # fn ok_value() -> Self::Ok {
254/// # ()
255/// # }
256/// # }
257/// #
258/// # impl<'de> Deserializer<'de> for u8 {
259/// # type Error = ();
260/// # }
261/// #
262/// # impl Serialize for u8 {
263/// # fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
264/// # where
265/// # S: Serializer
266/// # {
267/// # Ok(S::ok_value())
268/// # }
269/// # }
270/// #
271/// # impl<'de> Deserialize<'de> for u8 {
272/// # fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
273/// # where
274/// # D: Deserializer<'de>
275/// # {
276/// # Ok(0u8)
277/// # }
278/// # }
279/// use trait_set::trait_set;
280///
281/// trait_set! {
282/// pub trait Serde = Serialize + for<'de> Deserialize<'de>;
283/// // Note that you can also use a lifetime as a generic parameter.
284/// pub trait SerdeLifetimeTemplate<'de> = Serialize + Deserialize<'de>;
285/// }
286/// ```
287///
288/// [hrtb]: https://doc.rust-lang.org/nomicon/hrtb.html
289#[proc_macro]
290pub fn trait_set(tokens: TokenStream) -> TokenStream {
291 let input = parse_macro_input!(tokens as ManyTraitSet);
292 input.render().into()
293}