shed/trait_alias/src/lib.rs (34 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under both the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree and the Apache
* License, Version 2.0 found in the LICENSE-APACHE file in the root directory
* of this source tree.
*/
//! Procedural macro to enable trait aliases.
extern crate proc_macro;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Error, ItemTraitAlias};
/// Implement a trait alias using a subtrait and blanket definition.
///
/// Trait aliases are not yet available in stable Rust, and the preview
/// version has limitations: the additional traits can only be marker
/// traits.
///
/// Allow their implementation by translating them to a different
/// mechanism.
///
/// This macro converts an item like this:
///
/// ```no_run
/// # trait Bar {} trait Baz {} trait Quux {}
/// use trait_alias::trait_alias;
///
/// #[trait_alias]
/// trait Foo = Bar + Baz + Quux;
/// ```
///
/// into a sub-trait definition and a blanket implementation:
///
/// ```no_run
/// # trait Bar {} trait Baz {} trait Quux {}
/// trait Foo: Bar + Baz + Quux {}
///
/// impl<T> Foo for T where T: Bar + Baz + Quux {}
/// ```
///
/// Note that this approach has its own drawbacks, however it should work fine
/// for simple use cases.
#[proc_macro_attribute]
pub fn trait_alias(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let _ = parse_macro_input!(attr as syn::parse::Nothing);
let trait_alias = parse_macro_input!(item as ItemTraitAlias);
match gen_trait_alias(trait_alias) {
Ok(output) => output,
Err(e) => e.to_compile_error(),
}
.into()
}
fn gen_trait_alias(trait_alias: ItemTraitAlias) -> Result<TokenStream, Error> {
let vis = trait_alias.vis;
let ident = trait_alias.ident;
let bounds = trait_alias.bounds;
Ok(quote! {
#vis trait #ident : #bounds {}
impl<T> #ident for T where T: #bounds {}
})
}